Prepare to process the data

Background and Functions

Beginning information from original Dobrowski et al 2013 appendix code:

This script contains 4 functions used to model ET0 and water balance: 1. ‘snowmod’ estimates snowfall and snowpack and net moisture input as a function of temperature, precip, and existing snowpack. It also outputs a vector of albedo values, generally 0.2 if there is no snow, or 0.8 if there is snow. 2. ‘monthlyETO’ for calculating monthly reference evapotranspiration 3. ‘dailyET0’ for calculating daily reference evapotranspiration 4. ‘aetmod’ estimates actual et [evapotranspiration], deficit, soil moisture and runoff as a function of moisture input, existing soil moisture, and soil water capacity.

Author: Alan Swanson 2012

The third equation, daily ET0, is the one we are most interested in for the Ketapang climate data. The original code:

dailyET0_Dobrowski <- function(radiation,tmax,tmin,wind,lat,elev,albedo=0.23,dpt,doy){
  # This is a ET0 function designed for daily inputs.  
  # Arguments:
  # radiation: vector of monthly average shortwave radiation in MJ/m^2/day
  # tmax, tmin: vectors of monthly average maximum and minimum temperatures in C, 
  # wind: vector of monthly average wind speed in m/s, 
  # tmean_prev: vector of mean temp for the previous month, 
  # lat: vector of latitude in degrees 
  # elev: vector of elevation in meters, 
  # albedo: scaler or vector of albedo values, 
  # doy: scalar day of year 1-365,
  # dpt: vector of dewpoint temperature in C.
  #
  # Value: 
  # Returns a vector of ET0 values.
    tmean <- (tmin+tmax)/2
    n_days <- 1 
    G <- 0 # assume soil heat flux to be zero
    
    # wind adjustment to 2m from 10m output
    hw=10 # height at which wind is measured
  wind <- wind*(4.87/log(67*hw-5.42))  
  
  # stomatal conductance adjustment for low temperatures
  sr=100 # stomatal resistance sec/m
    ks_min=.01 # minimum value for temps below T1
    Tl=-10       # minimum temp (sc goes to ks_min below this temp)
    T0=5        # optimal temp
    Th=100     # maximum temp (sc goes to zero above this)
    thresh=5   # temperature threshold below which to apply Jarvis equation (ks=1 above this temp)
    b4=(Th-T0)/(Th-Tl)  # from Jarvis 1978
    b3=1/((T0-Tl)*(Th-T0)^b4)
    ks=pmax(pmin(b3*(tmean-Tl)*(Th-tmean)^b4,1),ks_min)
    ks[is.na(ks)] <- ks_min
    ks[tmean>=thresh] <- 1
        
    # convert to stomatal resistance.
    sr <- sr/ks
        
    # ra is aerodynamic resistance, rs is bulk surface resistance
    ra <- 208/wind # 
    rs <- sr/(0.5*24*0.12) # value of 70 when sr=100
    
    # Saturation vapor pressure , 
    es <- 0.6108*exp(tmin*17.27/(tmin+237.3))/2+0.6108*exp(tmax*17.27/(tmax+237.3))/2  # saturation vapor pressure
    ea <- 0.6108*exp((dpt)*17.27/((dpt)+237.3))                                        # actual vapor pressure
    vpd <- es - ea
    vpd[vpd<0] <- 0    
  
    # delta - Slope of the saturation vapor pressure vs. air temperature curve
    delta <- (4098 * es)/(tmean + 237.3)^2  
    P <- 101.3*((293-0.0065*elev)/293)^5.26  # Barometric pressure in kPa
    lambda <- 2.501-2.361e-3*tmean # latent heat of vaporization    
    cp <- 1.013*10^-3 # specific heat of air
    gamma <- cp*P/(0.622*lambda) # Psychrometer constant (kPa C-1)
    pa <- P/(1.01*(tmean+273)*0.287) # mean air density at constant pressure
    
    # Calculate potential max solar radiation or clear sky radiation.
    GSC = 0.082      # MJ m -2 min-1 (solar constant)
    phi <- pi*lat/180 
    dr <- 1+0.033*cos(2*pi/365*doy)      
    delt <- 0.409*sin(2*pi/365*doy-1.39)     
    omegas <- acos(-tan(phi)*tan(delt))     
    Ra <- 24*60/pi*GSC*dr*(omegas*sin(phi)*sin(delt) +cos(phi)*cos(delt)*sin(omegas)) # daily extraterrestrial radiation
    Rso <- Ra*(0.75+2e-5*elev)     #For a cloudless day, Rs is roughly 75% of extraterrestrial radiation (Ra)
    # radfraction is a measure of relative shortwave radiation, or of
    # possible radiation (cloudy vs. clear-sky), needs to be less than 1
    radfraction <- radiation/Rso
    radfraction[radfraction>1] <- 1
    
    # longwave radiation
    longw <- 4.903e-9*n_days*((tmax+273.15)^4+(tmin+273.15)^4)/2*(.34-.14*sqrt(ea))*(1.35*radfraction-.35)     
    
  # net radiation
    netrad <- radiation*n_days*(1-albedo)-longw     
    
    # ET from long-form P-M eqn.
    et0 <- .408*((delta*(netrad-G))+(pa*cp*vpd/ra*3600*24*n_days))/(delta+gamma*(1+rs/ra))
    return(et0)
} 

Dan also made a new version of the daily ET0 function. Add it also so we can compare the results. USE THIS ONE INSTEAD!!

dailyET0 <- function(lat,elev,psun,wind,doy,tmax,tmin,rh){
    # This is a ET0 function designed for daily inputs.  
  
  # Arguments:
  # psun: vector of daily percent sunshine
  # tmax, tmin: vectors of monthly average maximum and minimum temperatures in C, 
  # wind: vector of monthly average wind speed in m/s, 
  # tmean_prev: vector of mean temp for the previous month, 
  # lat: vector of latitude in degrees 
  # elev: vector of elevation in meters, 
  # rh: vector of relative humidity (mean daily).
  # tmean_prev: vector of mean temp of previous month in C
  # albedo: scaler or vector of albedo values, 
  # doy: scalar day of year 1-365,
  # Value: 
  # Returns a vector of ET0 values.
  
#some constants
n_days = 1
GSC = 0.082      # MJ/m2/min (solar constant)
albedo = 0.23 #(hypothetical grass reference crop)
G <- 0 # assume soil heat flux to be zero
hw=5 # height at which wind is measured
cp <- 1.013*10^-3 # specific heat of air
# Step 1    
  tmean <- (tmin+tmax)/2
  lambda <- 2.501-2.361e-3*tmean # latent heat of vaporization    
# Step 2: Mean daily solar radiation (Rs in units MJ/m2/day). See Step 12.  Nothing needed here.
# Step 3: wind adjustment to 2m from 10m measurements
  wind <- wind*(4.87/log(67*hw-5.42))  
  
# Step 10: Mean saturation vapor pressure (es)
    es <- 0.6108*exp(tmin*17.27/(tmin+237.3))/2+0.6108*exp(tmax*17.27/(tmax+237.3))/2  # saturation vapor pressure
# Step 4: Slope of the saturation vapor pressure vs. air temperature curve (delta)
    delta <- (4098 * es)/(tmean + 237.3)^2  
# Step 5: Atmospheric Pressure (P)
    P <- 101.3*((293-0.0065*elev)/293)^5.26  # Barometric pressure in kPa
# Step 6: Psychrometric constant (gamma)
    gmma <- cp*P/(0.622*lambda) # Psychrometer constant (kPa C-1)
#Step 7: Delta Term (dt) (auxiliary calculation for Radiation Term)
    dt <- delta/(delta+gmma*(1+0.34*wind))
# Step 8: Psi Term (pt) auxiliary calculation for Wind Term.
    pt <- gmma/(delta+gmma*(1+0.34*wind))
    
# Step 9: Temperature Term (auxiliary calculation for Wind Term)
    tt <- (900/(tmean+273))*wind
# Step 11: actual vapor pressure (ea)
    ea <- (rh/100)*(0.6108*exp(tmin*17.27/(tmin+237.3))+0.6108*exp(tmax*17.27/(tmax+237.3)))/2
    vpd <- es - ea
    vpd[vpd<0] <- 0    
# Step 12: The inverse relative distance Earth-Sun (dr) and solar declination (delt)
    # Calculate potential max solar radiation or clear sky radiation.
    dr <- 1+0.033*cos(2*pi/365*doy)      
    delt <- 0.409*sin(2*pi/365*doy-1.39)
# Step 13: Convert latitude to radians (phi)
    phi <- pi*lat/180 
# Step 14: Sunset hour angle (ws)
    omegas <- acos(-tan(phi)*tan(delt))
# Step 15: Daily extraterrestrial radiation (Ra)
    Ra <- 24*60/pi*GSC*dr*(omegas*sin(phi)*sin(delt) +cos(phi)*cos(delt)*sin(omegas))
# Step 16: Clear sky solar radiation (Rso). Rs is fraction of Rso
    Rso <- Ra*(0.75+2e-5*elev)     #For a cloudless day, Rs is roughly 75% of extraterrestrial radiation (Ra)
    # radfraction is a measure of relative shortwave radiation, needs to be less than 1
    radfraction <- psun
    radfraction[radfraction>1] <- 1
    Rs<-Rso*radfraction
# Step 17: Net solar or net shortwave radiation (Rns).  
    Rns  <- (1-albedo)*Rs
# Step 18: Net outgoing longwave solar radiation (Rnl)
    Rnl <- 4.903e-9*n_days*((tmax+273.15)^4+(tmin+273.15)^4)/2*(.34-.14*sqrt(ea))*(1.35*radfraction-.35)     
# Step 19: Net radiation (Rn)
    Rn <- Rns*n_days-Rnl     
# Final step
# Radiation term.  (0.408*Rn) is net radiation expressed in equivalent of evaporation (mm)  
    ETrad <- dt*0.408*Rn
# Wind term (ETwind)    
    ETwind <- pt * tt*(es-ea)
et0 <- ETwind + ETrad
return(data.frame(ETwind=ETwind,ETrad=ETrad,et0=et0))
}

And to calculate the soil water deficit, we need to use the fourth equation from Dobrowski et al. We already have the reference evapotranspiration, calculated in the previous function. We also have the monthly water input, and it’s already in the correct units (mm): precipiation. The function calls for soil water content from the previous month (day), but it will still function if it is left out.

aetmod_Dobrowski <- function(et0,input,awc,soil_prev=NULL){
  # This function computes AET given ET0, H2O input, soil water capacity, and beginning-of-month soil moisture
  # Arguments:
  # et0: vector of monthly reference evapotranspiration in mm
  # input: vector of monthly water input to soil in mm
  # awc: vector of soil water capacity in mm
  # soil_prev: vector of soil water content for the previous month (mm).  If left NULL this is assigned to be zero.
  #
  # Value:
  # returns a data frame with columns for AET, deficit, end-of-month soil moisture, and runoff.
  
  N <- length(et0)
  runoff <- def <-  aet <- soil <- rep(NA,N) # 
  if(is.null(soil_prev)) soil_prev <- rep(0,N)
  
  deltasoil <- input-et0 # positive=excess H2O, negative=H2O deficit
  
  # Case when there is a moisture surplus:
    Case <- deltasoil>=0
  if(sum(Case)>0){
    aet[Case] <- et0[Case]
    def[Case] <- 0
    soil[Case] <- pmin(soil_prev[Case]+deltasoil[Case],awc[Case])   # increment soil moisture, but not above water holding capacity
    runoff[Case] <- pmax(soil_prev[Case]+deltasoil[Case]-awc[Case],0) # when awc is exceeded, send the rest to runoff
  }
  
  # Case where there is a moisture deficit:  soil moisture is reduced
  Case <- deltasoil<0
  if(sum(Case)>0){
    soildrawdown <- soil_prev[Case]*(1-exp(-(et0-input)[Case]/awc[Case]))   # this is the net change in soil moisture (neg)
    aet[Case] <- pmin(input[Case] + soildrawdown,et0[Case])
    def[Case] <- et0[Case] - aet[Case]
    soil[Case] <- soil_prev[Case]-soildrawdown
    runoff[Case] <- 0
  }
  
  return(data.frame(aet=aet,def=def,soil=soil,runoff=runoff))
  
}

Note: couldn’t get the code from Dobrowski et al. to work, so here’s another actual evapotraspiration function from Dan, based on this website’s calculator: http://geog.uoregon.edu/envchange/software/AETcalculator.pdf. USE THIS ONE.

aetmod <- function(et0,precip,awc){
  # This function computes AET given ET0, H2O input, soil water capacity, and beginning-of-month soil moisture
  # Arguments:
  # et0: vector of monthly reference evapotranspiration in mm
  # input: vector of monthly water input to soil in mm
  # awc: single value for soil water capacity in mm
  #
  # Value:
  # returns a data frame with columns for AET, deficit, soil water content, and runoff.
  
  N <- length(et0)
  runoff <- def <- aet <- soil <- rep(NA,N) 
  w.previous <- awc  #start with at capacity
  
  for(i in 1:N){
    beta <- (w.previous/awc)/.7  ###  beta function (declining availability function)
    if(beta>1){
        beta <- 1
        } else {
        beta <- 1-exp(-6.68*w.previous/awc)
        }
    if(beta<0){ beta <-0 }
    Dd <- precip[i]-et0[i] # positive=excess H2O, negative=H2O deficit
    if(!is.na(Dd)){
        if(Dd<0){ # precip less than demand, lowered soil water, no runoff
            soil[i] <- w.previous+beta*Dd  ### soil moisture at end of day i
            runoff[i] <- 0
            aet[i] <- precip[i]+(w.previous-soil[i])  # aet=precip+lowered soil water
            def[i] <- et0[i]-aet[i]
            }
        if(Dd>0){  # precip more than demand, increased soil water, possibly runoff
            soil[i] <- w.previous+Dd  ### soil moisture at end of day i
            if(soil[i]>awc){
                runoff[i] <- soil[i]-awc
                soil[i] <- awc
                } else {
                runoff[i] <- 0
                }
            aet[i] <- et0[i]
            def[i] <- 0
            }       
        w.previous <- soil[i]
        } #endif
    } # next day
    return(data.frame(aet=aet,def=def,runoff=runoff,soil=soil))
  }

This just leaves soil water capacity as the last thing that needs to be figured out (soil water capacity = difference between field capacity and permanent wilting point). Soil in the Ketapang area is, according to the FAO-UNESCO Soils Map: JD9 2/3a –> Dystric Fluvisols, medium/fine, level to undulating

“Use: Dystric Fluvisols occur mainly in Sumatra, Java, Kalimantan, Irian Jaya, Sulawesi and the Moluccas. They are developed on recent fluviatile, marine and lacustrine deposits and occupy positions on river levees, present flood plains, deltas and lake shores where alluvium is derived from predominantly acid parent rocks. Their macrorelief is generally flat, although levees often have an undulating microrelief. Soils subject to deep flooding remain under mixed swamp forest. Along coasts, soils which are periodically inundated by sea water are mainly under mangrove vegetation, which is intensively used in places for charcoal making. Where flooding is not too deep, or where flood protection and drainage works have been constructed, these soils are intensively used for paddy cultivation. Levee soils are traditionally used for settlement sites with home gardens, orchards and banana plantations. The better-drained Dystric Fluvisols of northern Sumatra produce Deliwrapper tobacco, and rubber and oil palm give very high yields.

Suitability. Most Dystric Fluvisols are stratified, medium to fine textured, and poorly to very poorly drained, although better-drained soils occur on levees. Soil reaction is slightly to strongly acid. Organic matter content is variable, but generally moderateto high. These soils are low in bases, but respond well to moderate applications of nitrogen and phosphate fertilizers. Water control presents the main problem for the successful utilization of these soils. A combination of irrigation during the dry season and flood protection and drainage during the rainy season is required on poorly drained soils. Where this is feasible, two crops of rice a year can be achieved, giving high yields under good management involving the use of high-yielding varieties, regular fertilizer application, and the use of insecticides and pesticides. In drier areas where irrigation water is limited, rainfed rice followed by a summer legume crop or tobacco gives satisfactory results under good management. Rubber, oil palm and sugar cane thrive where drainage is adequate. Where no drainage or flood control is required, these soils may be cultivated under a wide range of food, fruit and industrial crops with moderate applications of fertilizers. Where flood control is not feasible, recent developments in the breeding of floating rice varieties seem promising. The better-drained levee soils are generally fully utilized for settlement sites, home gardens, orchards and banana plantations." –FAO UNESCO Soil Map of the World

Also, some useful notes on evapotranspiration from the FAO: http://www.fao.org/docrep/x0490e/x0490e04.htm]]

Read in libraries and the data; initial data exploration

Load libraries and create a mini-function for reading in dates.

# load libraries
# install the package if you don't already have it
if (!require("plyr")) {
     install.packages("plyr")
     library(plyr)
}    
if (!require("zoo")) {
     install.packages("zoo")
     library(zoo)
}
# if (!require("pheno")) {
#      install.packages("pheno")
#      library(pheno)
# }
## create a mini function that makes the date of the detected fire be read in as a POSIXct date, rather than as a character or a string
## note that because this data is from Indonesia, the day comes before the month
setClass('ddmmyyyy_KTG')
setAs("character","ddmmyyyy_KTG", function(from) as.Date(from, format="%d/%m/%Y",tz="UTC+7"))    # also sets the time zone; really just needed so we don't get an error because all of this data is from the same spot

We need to read in the climate data from Ketapang, and perform a couple of calculations to get everything into the right units and format.

# read in the Ketapang climate data
## no data is given the value 9999 in the original data; na.strings converts indicated values to NA (which is what R uses to indicate no data) without any extra steps
## 9999 is no data
## 8888 is no measurable data. not sure how this is different than no data... maybe these should actually be 0?
KTG<-read.csv("/Users/laurenhendricks/Documents/Borneo/Ketapang_ClimateFire/climate_data/KetapangClimate1986_2016 COPY.csv",header=T,sep=",",na.strings=c("9999","8888"),colClasses = c("character","factor","ddmmyyyy_KTG",rep("numeric",7),"factor","numeric","factor") )
# rename the columns into english
colnames(KTG)<-c("station","stationID","date","T_min","T_max","T_avg","humidity","precip","sunshine","wind_speed_knots","wind_dir","wind_maxgust_knots","wind_maxgust_dir")
head(KTG)

We already have: * Maximum temperature in Celsisus; * Minimum temperature in Celsius; and * Humidity (assuming it is relative humidity).

We also have: * Wind speed, but it needs to be converted from knots to meters per second; * Date, which needs to be converted to the day of the year (aka Julian date); and * Hours of sunshine.

We need to calcluate: * Dewpoint (can be calculated from temperature); and * Solar radiation (can be calcluated from the hours of sunshine).

We can use some simplifications to calculate these more easily; need to check and see if these simplifications are valid.

But first, some quick data exploration.

# look at the sunshine data  
boxplot(KTG$sunshine,main="Hours of Sunshine")

# look at the temperature data  
boxplot(KTG$T_min,main="Minimum Temperature")

boxplot(KTG$T_max,main="Maximum Temperature")

boxplot(KTG$T_avg,main="Average Temperature")

# look at the humidity data
boxplot(KTG$humidity,main="Relative Humidity")

# precip data
boxplot(KTG$precip,main="Daily Precipitation")

# wind data
boxplot(KTG$wind_speed_knots,main="Wind Speed")

Looks like the data is definitely not normally distributed for most of these variables, but since we’re not trying to do any statistical analyses on these it shouldn’t be a problem. We also need to note that there are some clear problems in the data.

Massaging the data (dealing with NAs and other non-sensical values)

There also seem to be some issues with the precipitation data–there is a LOT of probably missing data–but those are even harder to track down, so just read them in as 0 for now.

Another thing that might need to be dealt with has to do with the windspeed. Windspeed varies with height, so we need to find out the height that the measurement was taken at. There are also a lot of missing values.

Calculate a moving average that we can then use to fill in some of the missing values. This isn’t the ideal approach, but we have to have some value–otherwise the AET for that day will be modeled as 0, which is even less realistic than filling it in with modeled/smoothed data. (Note that for some things, like temperature, a moving average does seem reasonable, but it makes less sense for other things, like wind and precipitation.)

# first count the number of NAs in the columns to identify which need to have a moving average applied
print(paste(sum(is.na(KTG$T_min)),"NAs in T_min"))
[1] "79 NAs in T_min"
print(paste(sum(is.na(KTG$T_max)),"NAs in T_max"))
[1] "103 NAs in T_max"
print(paste(sum(is.na(KTG$T_avg)),"NAs in T_avg"))
[1] "80 NAs in T_avg"
print(paste(sum(is.na(KTG$humidity)),"NAs in humidity"))
[1] "81 NAs in humidity"
print(paste(sum(is.na(KTG$precip)),"NAs in precip"))
[1] "651 NAs in precip"
print(paste(sum(is.na(KTG$sunshine)),"NAs in sunshine hours"))
[1] "124 NAs in sunshine hours"
print(paste(sum(is.na(KTG$wind_speed_knots)),"NAs in wind speed"))
[1] "1881 NAs in wind speed"
# not checking for wind/gust direction because we're not using that anywhere

So, looks like we need to use the moving average for every variable of interest, though there are A LOT more missing values for wind than for any of the other measurements.

So, we can use a moving average (mean) to fill in the missing values. Use rollapply() from the zoo package. Could also check out na.approx() or na.spline() in the zoo package, which could be an interesting comparison. Though, with some initial testing, it seems like it’s not flexible enough? It has issues with NA values. We could also try na.aggregate() which replaces NAs with aggregated values, so we could replace NAs with something like a montly mean. This is probably not better than a moving average. Yet another option is given by na.locf() which fills in the NAs with the last non-NA value.

# make a data frame to store the rolling means in
# also have the original data in there NEXT TO THE ROLLING MEAN so it's easier to compare the results
KTG_rollmeans<-data.frame(matrix(nrow=length(KTG$date),ncol=1+(7*2)))    # same number of rows as original, and then 2 columns for each of the 7 variables and have a column for the date
# add in the dates
KTG_rollmeans[,1]<-KTG$date
# set the window
window<-10    
KTG_rollmeans[,2]<-KTG$T_min
KTG_rollmeans[,3]<-rollapply(KTG$T_min,window,FUN=mean,fill=NA,partial=1,na.rm=T)
KTG_rollmeans[,4]<-KTG$T_max
KTG_rollmeans[,5]<-rollapply(KTG$T_max,window,FUN=mean,fill=NA,partial=1,na.rm=T) 
KTG_rollmeans[,6]<-KTG$T_avg
KTG_rollmeans[,7]<-rollapply(KTG$T_avg,window,FUN=mean,fill=NA,partial=1,na.rm=T) 
KTG_rollmeans[,8]<-KTG$humidity
KTG_rollmeans[,9]<-rollapply(KTG$humidity,window,FUN=mean,fill=NA,partial=1,na.rm=T) 
KTG_rollmeans[,10]<-KTG$precip
KTG_rollmeans[,11]<-rollapply(KTG$precip,window,FUN=mean,fill=NA,partial=1,na.rm=T) 
KTG_rollmeans[,12]<-KTG$sunshine
KTG_rollmeans[,13]<-rollapply(KTG$sunshine,window,FUN=mean,fill=NA,partial=1,na.rm=T) 
KTG_rollmeans[,14]<-KTG$wind_speed_knots
KTG_rollmeans[,15]<-rollapply(KTG$wind_speed_knots,window,FUN=mean,fill=NA,partial=1,na.rm=T) 
# rename the columns
# will be combo of original names plus noting that it's a rolling mean
names_og<-colnames(KTG)[-(c(1,2,3,11,12,13))]     # get the original names but don't need first two columns (station name & id) or the columns with wind directions. don't need date either because we don't want to append roll mean to that
# also not going to use wind gusts so don't include that
names_new<-paste(names_og,"rollmean10",sep="_")   # add rolling mean to the names
# now a for loop to combine the original and new names in the correct order
names_rollmeans<-NA      # where the new names will be stored
i<-1      # counter that keeps track of which index in the new combined names vector we are at
for(col in 1:length(names_new)){   # col keeps track of index in the vectors we're pulling from 
    names_rollmeans[i]<-names_og[col]
    i<-i+1
    names_rollmeans[i]<-names_new[col]
    i<-i+1
}
# add date at the beginning
names_rollmeans<-c("date",names_rollmeans)
# now rename the columns
colnames(KTG_rollmeans)<-names_rollmeans
# print the new number of NAs (though they're acutally NaNs, not NAs, because that is what rollsum() puts out.)
print(paste(sum(is.na(KTG_rollmeans$T_min_rollmean10)),"NAs in T_min_rollmean10"))
[1] "43 NAs in T_min_rollmean10"
print(paste(sum(is.na(KTG_rollmeans$T_max_rollmean10)),"NAs in T_max_rollmean10"))
[1] "43 NAs in T_max_rollmean10"
print(paste(sum(is.na(KTG_rollmeans$T_avg_rollmean10)),"NAs in T_avg_rollmean10"))
[1] "43 NAs in T_avg_rollmean10"
print(paste(sum(is.na(KTG_rollmeans$humidity_rollmean10)),"NAs in humidity_rollmean10"))
[1] "43 NAs in humidity_rollmean10"
print(paste(sum(is.na(KTG_rollmeans$precip_rollmean10)),"NAs in precip_rollmean10"))
[1] "44 NAs in precip_rollmean10"
print(paste(sum(is.na(KTG_rollmeans$sunshine_rollmean10)),"NAs in sunshine_rollmean10"))
[1] "43 NAs in sunshine_rollmean10"
print(paste(sum(is.na(KTG_rollmeans$wind_speed_knots_rollmean10)),"NAs in windspeed_rollmean10"))
[1] "50 NAs in windspeed_rollmean10"
# not checking for wind/gust direction because we're not using that anywhere

NOTE: There seem to be a few periods where the entire weather station must have been down, because there are no values at all for any of the measurements for those days. Even roll sum can’t deal with that…. unless we use a huge window. Those time periods are: July 2000 and September 1997 (the whole months). To fill all those in the window would need to be over 30 days… and that’s probably too big! So for the 20 and 21 days in the middle of the month we don’t have any values (because the first and last 5 do have numbers, pulled in from the previous and following months by the roll apply function). Plus, there are several 10+day periods where no wind measurements were taken: Jan 11- 21 1991, April 2-13 1991, April 28-May 8 1991, and Nov 27-Dec 6 1992. (Also note that there are a lot of missing values on either side of those periods, too, but there is at least 1 measurement for every 10 apart from those chunks.) Weirdly, in a lot of these periods, there IS precipitation data but the values are all 0. (So maybe this isn’t accurate data? Checked in the raw data and they really are 0s, not 8888/9999s.) And the 44 NAs/NaNs for precip are in 2015, not in those other long stretches–it’s basically the second half of 2015, with a few days here and there where there is data so it isn’t fully NaNs. Sooo…don’t use those chunks of data!

So, there are some limitations even after filling in the missing values with moving averages… but we have to go ahead and fill in those values anyways.

# now make the replacements! 
# figured out how to do this with the toy data in /Users/laurenhendricks/Documents/Borneo/Ketapang_ClimateFire/code/SandboxCode/FillMissingValues.R
KTG$T_min[is.na(KTG$T_min)]<-KTG_rollmeans$T_min_rollmean10[is.na(KTG$T_min)]
KTG$T_max[is.na(KTG$T_max)]<-KTG_rollmeans$T_max_rollmean10[is.na(KTG$T_max)]
KTG$T_avg[is.na(KTG$T_avg)]<-KTG_rollmeans$T_avg_rollmean10[is.na(KTG$T_avg)]
KTG$precip[is.na(KTG$precip)]<-KTG_rollmeans$precip_rollmean10[is.na(KTG$precip)]
KTG$humidity[is.na(KTG$humidity)]<-KTG_rollmeans$humidity_rollmean10[is.na(KTG$humidity)]
KTG$sunshine[is.na(KTG$sunshine)]<-KTG_rollmeans$sunshine_rollmean10[is.na(KTG$sunshine)]
KTG$wind_speed_knots[is.na(KTG$wind_speed_knots)]<-KTG_rollmeans$wind_speed_knots_rollmean10[is.na(KTG$wind_speed_knots)]
# also add in a column that flags where there are NAs filled in with another value in at least one of the other columns
KTG$flag<-NA
KTG$flag[is.na(KTG$T_min)]<-"One or more estimated values"
KTG$flag[is.na(KTG$T_max)]<-"One or more estimated values"
KTG$flag[is.na(KTG$T_avg)]<-"One or more estimated values"
KTG$flag[is.na(KTG$precip)]<-"One or more estimated values"
KTG$flag[is.na(KTG$humidity)]<-"One or more estimated values"
KTG$flag[is.na(KTG$sunshine)]<-"One or more estimated values"
KTG$flag[is.na(KTG$wind_speed_knots)]<-"One or more estimated values"

Now check to see if we have the NAs filled in.

# check to see if the number of filled in NAs is less than before
# (ideally it would be 0 but because of the issue noted earlier about the 10+ day stretches of no values we won't ever see 0)
print(paste(sum(is.na(KTG$T_min)),"NAs in T_min"))
[1] "43 NAs in T_min"
print(paste(sum(is.na(KTG$T_max)),"NAs in T_max"))
[1] "43 NAs in T_max"
print(paste(sum(is.na(KTG$T_avg)),"NAs in T_avg"))
[1] "43 NAs in T_avg"
print(paste(sum(is.na(KTG$humidity)),"NAs in humidity"))
[1] "43 NAs in humidity"
print(paste(sum(is.na(KTG$precip)),"NAs in precip"))
[1] "44 NAs in precip"
print(paste(sum(is.na(KTG$sunshine)),"NAs in sunshine hours"))
[1] "43 NAs in sunshine hours"
print(paste(sum(is.na(KTG$wind_speed_knots)),"NAs in wind speed"))
[1] "50 NAs in wind speed"

So, it’s not perfect but it IS better than the original data! Might need to do something like break this into sections that start and stop before and after the big chunks of missing data…. Need to talk with Dan about that. But that shouldn’t affect us until we get to the aet coalculations, since that does rely on the values from previous days.

But, we have to move ahead with all of the conversions, etc., anyways.

Calcluate ET0

For all of these calcuations, assume that: * Airport location is -1.816, 109.963 (1.816 S, 109.963 E) * Airport elevation is 14 meters

Useful constants/conversions: * Windspeed in meters per second = 0.5144444 * windspeed in knots * 1 KWh=3.6 MJ (for radiation) aka 1 watt-hour = 3600 joules = .0036 Mjoules

One obvious problem that a moving average can’t at all fix is related to the hours of sunshine. For most days the max reported amount of sun is 8 hours, but this doesn’t make sense with what we know about the place, which is that there is ~12 hours of sun possible a day, and it definitely isn’t always cloudy for 4 hours every day in Ketapang. Also, there is one day with 13.5 hours of sunshine (18 Oct 2016) and another with 12.5 hours of sunshine (15 Jul 2016). So it seems likely that there are some errors in the data, but we don’t know if they are random or systematic (e.g., data entry or a bigger problem in how they are measuring hours of sunshine)! Looking at the raw data, it seems like the 8 hour max issue is probably something with the detector, since ~2015 the numbers start to make more sense; maybe ~2015 they got a better detector? But, a quick solution that still allows us to use all of the data is to normalize all the sunshine data to a maximum of 8 hours.

To calculate solar radiation, we have information on radiation and hours of sunshine from NASA Surface Meteorology and Solar Energy (https://eosweb.larc.nasa.gov/cgi-bin/sse/grid.cgi): * The monthly averaged clear sky insolation incident on a horizontal surface (kWh/m2/day) for airport location from 22 year average is: (Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec) = (7.33,7.55,7.51,7.26,6.80,6.50,6.57,6.91,7.28,7.40,7.33,7.20) * The monthly averaged daylight hours for airport location from 22 year average is: (Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec) = (12.2,12.1,12.1,12.0,12.0,12.0,12.0,12.0,12.1,12.1,12.2,12.2)

To calculate dewpoint, use simplified equation from Lawrence 2005 (may later want to use a more precise equation). This uses relative humidity (so assuming that the values in the spreadsheed are relative, not absolute!) and temperature. The equation is: dewpoint temperature = T - ((100 - relative humdiity)/5).

###################### Day of year
# convert the date into the day of the year for the function
KTG$doy<-as.numeric(strftime(KTG$date, format = "%j"))
#head(KTG$doy)
###################### Windspeed (in knots)
# note that the wind speed is given in knots and it needs to be converted to m/s
# windspeed in meters per second = 0.5144444 * windspeed in knots
KTG$wind_speed_ms<-0.5144444*KTG$wind_speed_knots
###################### Solar radiation
# calcluate solar radiation from hours of sunshine
###### treat 8 as the max number of hours in the day and reduce everything that is more than 8 hours down to 8
KTG$psun<-ifelse(KTG$sunshine<8,(KTG$sunshine/8),1)
KTG$sunshine_norm<-8*KTG$psun
###### then to turn it into radiation
# use the clear sky insolation, hours of daylight, and (normalized) sunshine hours to calcuate a rough radiation
# monthly averaged clear sky insolation incident on a horizontal surface (kWh/m2/day) for airport location: (Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec)=(7.33,7.55,7.51,7.26,6.80,6.50,6.57,6.91,7.28,7.40,7.33,7.20)
# monthly averaged daylight hours for airport location (-1.816, 109.963) from 22 year average is: (Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec)=(12.2,12.1,12.1,12.0,12.0,12.0,12.0,12.0,12.1,12.1,12.2,12.2)
# 1 watt-hour = 3600 joules = .0036 Mjoules
# both of these sets of values are from NASA Surface Meteorology and Solar Energy (https://eosweb.larc.nasa.gov/cgi-bin/sse/grid.cgi)
# make a data frame with the month, insolation, and hours of daylight
month<-seq(1,12,1)  # the month number
insolation<-c(7.33,7.55,7.51,7.26,6.80,6.50,6.57,6.91,7.28,7.40,7.33,7.20) # the insolation values
daylight<-c(12.2,12.1,12.1,12.0,12.0,12.0,12.0,12.0,12.1,12.1,12.2,12.2)   # the maximum daylight hours
## note that we could also use the daylength function from the pheno package to calculate the daylight hours
tmp<-cbind.data.frame(month,insolation,daylight)
# pull out just the month from the dates
KTG$month<-as.numeric(format(KTG$date,"%m"))
#head(KTG$month)  # check to make sure it's running correctly   
# then use "join" from the plyr package to combine the two data sets based on the month value, and also preserve the original row order
KTG<-join(KTG,tmp,by="month")
# last, calculate the radiation based on the percentage of total daylight hours sunshine was observed multiplied by the clear sky insolation, and then convert it to MJ (1KWh=3.6MJ)
KTG$radiation<-(((KTG$psun)*KTG$daylight)*KTG$insolation)*3.6
# calculates the hours of sunshine as a percentage and then converts that to what it would be if it was the real daylight hours... this is a big assumption!!!!
# remove the intermediate columns we added for month, insolation, and hours of daylight
KTG$month<-NULL
KTG$insolation<-NULL
KTG$daylight<-NULL
###################### Latitude
# Ketapang airport is at approximately 1.8164°S --> -1.8164
lat<- -1.8164
###################### Elevation
# Ketapang airport elevation is approximately 14 meters (need to double check this)
elev<-14
###################### Dewpoint
# calculate dewpoint using the simplification presented in Lawrence 2005 (may later want to use a more precise equation)
# this uses relative humidity (so assuming that the values in the spreadsheed are relative, not absolute!) and temperature
# dewpoint temperature = T - ((100 - relative humdiity)/5)
# T is the minimum temperature (check this)
KTG$dewpoint<-KTG$T_min-((100-KTG$humidity)/5)
#head(KTG$dewpoint)

Because the aet function takes values from previous day(s) into account, cut down to only the time periods that we know are good before calculating the aet. Let’s take November 2001 through February 2015.

KTG_00to15<-KTG[(KTG$date>=as.Date("01/11/2000", format="%d/%m/%Y",tz="UTC+7") & KTG$date<as.Date("03/01/2015", format="%d/%m/%Y",tz="UTC+7")),]

Then do the calculation for reference evapotranspiration, using both the function directly from Dobrowski et al and the one from Dan. Ideally they would come up with the same numbers and plotting them would result in a straight line.

# using the Dobrowski et al function calculate reference evapotranspiration
ET0_output<-dailyET0(lat=lat,elev=elev,psun=KTG_00to15$psun,wind=KTG_00to15$wind_speed_ms,doy=KTG_00to15$doy,tmax=KTG_00to15$T_max,tmin=KTG_00to15$T_min,rh=KTG_00to15$humidity)
# using Dobrowski et al version
ET0_output_Dobrowski<-dailyET0_Dobrowski(radiation=KTG_00to15$radiation,tmax=KTG_00to15$T_max,tmin=KTG_00to15$T_min,wind=KTG_00to15$wind_speed_ms,lat=lat,elev=elev,dpt=KTG_00to15$dewpoint,doy=KTG_00to15$doy)
     
     
# then compare them. 
plot(ET0_output$et0,ET0_output_Dobrowski/10)

# the agreement is generally pretty good though it's still off by a factor of 10, need to figure this out eventually

Clearly, there are some big differences in the assumptions made, which is putting things on different scales–seems like the Dobrowski et al. version is 10 times higher than Dan’s–but other than that, the relationship is pretty linear. Seems off by a factor of ~10. Not yet sure why… but most likely due to either the radiation or the dewpoint calcluations, because those are the only different inputs (calculated inside Dan’s function, but given as inputs to the Dobrowski function).

DAN SAYS TO USE HIS FUNCTION BECAUSE THE NUMBERS ARE MORE REASONABLE.

Calcluate actual evapotranspiration (aet), deficit, etc.

Then calculate actual evapotranspiration etc. using Dan’s function.

# just trying a random value for awc --> it's the average value in http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.461.1371&rep=rep1&type=pdf
soilbalance122<-aetmod(et0=ET0_output$et0,precip=KTG_00to15$precip,awc=122)
# also try some other numbers
soilbalance50<-aetmod(et0=ET0_output$et0,precip=KTG_00to15$precip,awc=50)
soilbalance80<-aetmod(et0=ET0_output$et0,precip=KTG_00to15$precip,awc=80)
soilbalance100<-aetmod(et0=ET0_output$et0,precip=KTG_00to15$precip,awc=100)

Now, write out the data after adding in the ET0 and soil-water balance information.

# put the et0 and aet data into the data frame with all the other data
KTG_00to15$ET0<-ET0_output$et0
KTG_00to15$aet50<-soilbalance50$aet
KTG_00to15$deficit50<-soilbalance50$def
KTG_00to15$soil50<-soilbalance50$soil
KTG_00to15$aet80<-soilbalance80$aet
KTG_00to15$deficit80<-soilbalance80$def
KTG_00to15$soil80<-soilbalance80$soil80
KTG_00to15$aet100<-soilbalance100$aet
KTG_00to15$deficit100<-soilbalance100$def
KTG_00to15$soil100<-soilbalance100$soil
KTG_00to15$aet122<-soilbalance122$aet
KTG_00to15$deficit122<-soilbalance122$def
KTG_00to15$soil122<-soilbalance122$soil
# remove a few columns that aren't important for anythign in the future before writing out the data
KTG_00to15$wind_maxgust_knots<-NULL
KTG_00to15$wind_dir<-NULL
KTG_00to15$wind_maxgust_dir<-NULL
KTG_00to15$wind_speed_knots<-NULL
KTG_00to15$doy<-NULL
write.table(KTG_00to15,"/Users/laurenhendricks/Documents/Borneo/Ketapang_ClimateFire/Ketapang_ET0andAET_00to15.txt",sep=",",row.names = F)

Combining ET0/aet with MODIS active fire detections

Okay! Now we’re going to read in the new data (even though it’s already in the memory) so that in future we can just start at this code chunk.

## create a mini function that makes the date of the detected fire be read in as a POSIXct date, rather than as a character or a string or a factor
# it's been reformatted by R when it was converted to a table before so we can't use the same function
setClass('yyyy-mm-dd')
setAs("character","yyyy-mm-dd", function(from) as.Date(from, format="%Y-%m-%d",tz="UTC+7"))    # also sets the time zone; really just needed so we don't get an error because all of this data is from the same spot
# now read in the data
weather_00to15<-read.csv("/Users/laurenhendricks/Documents/Borneo/Ketapang_ClimateFire/Ketapang_ET0andAET_00to15.txt",header=T,colClasses = c("factor","factor","yyyy-mm-dd",rep("numeric",6),"character",rep("numeric",17)))
# double check to make sure that this  DOES get rid of all of the big NA runs
print(paste(sum(is.na(weather_00to15$T_min)),"NAs in T_min"))
[1] "0 NAs in T_min"
print(paste(sum(is.na(weather_00to15$T_max)),"NAs in T_max"))
[1] "0 NAs in T_max"
print(paste(sum(is.na(weather_00to15$T_avg)),"NAs in T_avg"))
[1] "0 NAs in T_avg"
print(paste(sum(is.na(weather_00to15$humidity)),"NAs in humidity"))
[1] "0 NAs in humidity"
print(paste(sum(is.na(weather_00to15$precip)),"NAs in precip"))
[1] "0 NAs in precip"
print(paste(sum(is.na(weather_00to15$sunshine)),"NAs in sunshine hours"))
[1] "0 NAs in sunshine hours"
print(paste(sum(is.na(weather_00to15$wind_speed_ms)),"NAs in wind speed"))
[1] "0 NAs in wind speed"
print(paste(sum(is.na(weather_00to15$ET0)),"NAs in ET0"))
[1] "0 NAs in ET0"

So, we don’t have any NA values! Yay!

Let’s do some visualizing of the data.

# et0
plot(weather_00to15$date,weather_00to15$ET0,type="p",pch=16,cex=0.25,ylab="ET0 ",xlab="Date", main="ET0 Over Time")

# AET 50, 80, 100, 122 (separately)
plot(weather_00to15$date,weather_00to15$aet50,type="p",pch=16,cex=0.25,ylab="AET = 50",xlab="Date", main="AET Over Time, AWC = 50")

plot(weather_00to15$date,weather_00to15$aet80,type="p",pch=16,cex=0.25,ylab="AET = 80",xlab="Date", main="AET Over Time, AWC = 80")

plot(weather_00to15$date,weather_00to15$aet100,type="p",pch=16,cex=0.25,ylab="AET = 100",xlab="Date", main="AET Over Time, AWC = 100")

plot(weather_00to15$date,weather_00to15$aet122,type="p",pch=16,cex=0.25,ylab="AET = 122",xlab="Date", main="AET Over Time, AWC = 122")

# just for fun look at ET0 vs aet
plot(weather_00to15$ET0,weather_00to15$aet50,type="p",pch=16,cex=0.25,ylab="AET = 50",xlab="Date", main="ET0 vs. AET, AWC = 50")

plot(weather_00to15$ET0,weather_00to15$aet80,type="p",pch=16,cex=0.25,ylab="AET = 80",xlab="Date", main="ET0 vs. AET, AWC = 80")

plot(weather_00to15$ET0,weather_00to15$aet100,type="p",pch=16,cex=0.25,ylab="AET = 100",xlab="Date", main="ET0 vs. AET, AWC = 100")

plot(weather_00to15$ET0,weather_00to15$aet122,type="p",pch=16,cex=0.25,ylab="AET = 122",xlab="Date", main="ET0 vs. AET, AWC = 122")

# also plot soil deficit instead of aet
plot(weather_00to15$date,weather_00to15$deficit50,type="p",pch=16,cex=0.25,ylab="Deficit, AWC = 50",xlab="Date", main = "Deficit over time, AWC = 50")

plot(weather_00to15$date,weather_00to15$deficit80,type="p",pch=16,cex=0.25,ylab="Deficit, AWC = 80",xlab="Date", main = "Deficit over time, AWC = 80")

plot(weather_00to15$date,weather_00to15$deficit100,type="p",pch=16,cex=0.25,ylab="Deficit, AWC = 100",xlab="Date", main = "Deficit over time, AWC = 100")

plot(weather_00to15$date,weather_00to15$deficit122,type="p",pch=16,cex=0.25,ylab="Deficit, AWC = 122",xlab="Date", main = "Deficit over time, AWC = 122")

Now combine with the MODIS data.

Read in the necessary libraries, and some code to read in the fire detections that have already been cut down to just the area within the buffer, and make the columns be the correct class.

# load libraries
# install the package if you don't already have it
if (!require("plyr")) {
     install.packages("plyr")
     library(plyr)
     }
Loading required package: plyr
Warning message:
In format.POSIXlt(as.POSIXlt(x), ...) :
  unknown timezone 'zone/tz/2018c.1.0/zoneinfo/America/Los_Angeles'
# then the MODIS data
fire_500km<-read.csv("/Users/laurenhendricks/Documents/Borneo/Ketapang_ClimateFire/FRP_daily_500km.txt",header=T,sep=",",colClasses =c("yyyy-mm-dd","numeric"))
Error in methods::as(data[[i]], colClasses[i]) : 
  no method or default for coercing “character” to “yyyy-mm-dd”

Then combine the data sets.

# combine teh two data sets by the date, keeping the weather data even if there is no reported FRP for a day
weather_fire<-join(fire_500km_00to15,weather_00to15,by="date",type="full",match="all")

# if we wanted  order it by date this is how we'd do it
ordered_weather_fire<-weather_fire[order(weather_fire$Date),]

Then plot things!!!

# then plot it! 
plot(weather_fire$aet50,weather_fire$Total_FRP,type="p",pch=16,cex=0.5)

plot(weather_fire$deficit50,weather_fire$Total_FRP,type="p",pch=16,cex=0.5)

plot(weather_fire$aet80,weather_fire$Total_FRP,type="p",pch=16,cex=0.5)

plot(weather_fire$deficit80,weather_fire$Total_FRP,type="p",pch=16,cex=0.5)

plot(weather_fire$aet100,weather_fire$Total_FRP,type="p",pch=16,cex=0.5)

plot(weather_fire$deficit100,weather_fire$Total_FRP,type="p",pch=16,cex=0.5)

plot(weather_fire$aet122,weather_fire$Total_FRP,type="p",pch=16,cex=0.5)

plot(weather_fire$deficit122,weather_fire$Total_FRP,type="p",pch=16,cex=0.5)

LS0tCnRpdGxlOiAiTW9kZWxpbmcgRXZhcG90cmFuc3BpcmF0aW9uIGZyb20gV2VhdGhlciBEYXRhIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCiMgUHJlcGFyZSB0byBwcm9jZXNzIHRoZSBkYXRhIAojIyBCYWNrZ3JvdW5kIGFuZCBGdW5jdGlvbnMKCkJlZ2lubmluZyBpbmZvcm1hdGlvbiBmcm9tIG9yaWdpbmFsIERvYnJvd3NraSBldCBhbCAyMDEzIGFwcGVuZGl4IGNvZGU6CgogVGhpcyBzY3JpcHQgY29udGFpbnMgNCBmdW5jdGlvbnMgdXNlZCB0byBtb2RlbCBFVDAgYW5kIHdhdGVyIGJhbGFuY2U6CjEuICdzbm93bW9kJyBlc3RpbWF0ZXMgc25vd2ZhbGwgYW5kIHNub3dwYWNrIGFuZCBuZXQgbW9pc3R1cmUgaW5wdXQgYXMgYSBmdW5jdGlvbiBvZiB0ZW1wZXJhdHVyZSwgcHJlY2lwLCBhbmQgZXhpc3Rpbmcgc25vd3BhY2suICBJdCBhbHNvIG91dHB1dHMgYSB2ZWN0b3Igb2YgYWxiZWRvIHZhbHVlcywgZ2VuZXJhbGx5IDAuMiBpZiB0aGVyZSBpcyBubyBzbm93LCBvciAwLjggaWYgdGhlcmUgaXMgc25vdy4KMi4gJ21vbnRobHlFVE8nIGZvciBjYWxjdWxhdGluZyBtb250aGx5IHJlZmVyZW5jZSBldmFwb3RyYW5zcGlyYXRpb24KMy4gJ2RhaWx5RVQwJyBmb3IgY2FsY3VsYXRpbmcgZGFpbHkgcmVmZXJlbmNlIGV2YXBvdHJhbnNwaXJhdGlvbgo0LiAnYWV0bW9kJyBlc3RpbWF0ZXMgYWN0dWFsIGV0IFtldmFwb3RyYW5zcGlyYXRpb25dLCBkZWZpY2l0LCBzb2lsIG1vaXN0dXJlIGFuZCBydW5vZmYgYXMgYSBmdW5jdGlvbiBvZiBtb2lzdHVyZSBpbnB1dCwgZXhpc3Rpbmcgc29pbCBtb2lzdHVyZSwgYW5kIHNvaWwgd2F0ZXIgY2FwYWNpdHkuCgpBdXRob3I6IEFsYW4gU3dhbnNvbiAyMDEyCgpUaGUgdGhpcmQgZXF1YXRpb24sIGRhaWx5IEVUMCwgaXMgdGhlIG9uZSB3ZSBhcmUgbW9zdCBpbnRlcmVzdGVkIGluIGZvciB0aGUgS2V0YXBhbmcgY2xpbWF0ZSBkYXRhLiBUaGUgb3JpZ2luYWwgY29kZToKYGBge3J9CmRhaWx5RVQwX0RvYnJvd3NraSA8LSBmdW5jdGlvbihyYWRpYXRpb24sdG1heCx0bWluLHdpbmQsbGF0LGVsZXYsYWxiZWRvPTAuMjMsZHB0LGRveSl7CiAgIyBUaGlzIGlzIGEgRVQwIGZ1bmN0aW9uIGRlc2lnbmVkIGZvciBkYWlseSBpbnB1dHMuICAKICAjIEFyZ3VtZW50czoKICAjIHJhZGlhdGlvbjogdmVjdG9yIG9mIG1vbnRobHkgYXZlcmFnZSBzaG9ydHdhdmUgcmFkaWF0aW9uIGluIE1KL21eMi9kYXkKICAjIHRtYXgsIHRtaW46IHZlY3RvcnMgb2YgbW9udGhseSBhdmVyYWdlIG1heGltdW0gYW5kIG1pbmltdW0gdGVtcGVyYXR1cmVzIGluIEMsIAogICMgd2luZDogdmVjdG9yIG9mIG1vbnRobHkgYXZlcmFnZSB3aW5kIHNwZWVkIGluIG0vcywgCiAgIyB0bWVhbl9wcmV2OiB2ZWN0b3Igb2YgbWVhbiB0ZW1wIGZvciB0aGUgcHJldmlvdXMgbW9udGgsIAogICMgbGF0OiB2ZWN0b3Igb2YgbGF0aXR1ZGUgaW4gZGVncmVlcyAKICAjIGVsZXY6IHZlY3RvciBvZiBlbGV2YXRpb24gaW4gbWV0ZXJzLCAKICAjIGFsYmVkbzogc2NhbGVyIG9yIHZlY3RvciBvZiBhbGJlZG8gdmFsdWVzLCAKICAjIGRveTogc2NhbGFyIGRheSBvZiB5ZWFyIDEtMzY1LAogICMgZHB0OiB2ZWN0b3Igb2YgZGV3cG9pbnQgdGVtcGVyYXR1cmUgaW4gQy4KCiAgIwogICMgVmFsdWU6IAogICMgUmV0dXJucyBhIHZlY3RvciBvZiBFVDAgdmFsdWVzLgoJdG1lYW4gPC0gKHRtaW4rdG1heCkvMgoJbl9kYXlzIDwtIDEgCglHIDwtIDAgIyBhc3N1bWUgc29pbCBoZWF0IGZsdXggdG8gYmUgemVybwoJCgkjIHdpbmQgYWRqdXN0bWVudCB0byAybSBmcm9tIDEwbSBvdXRwdXQKCWh3PTEwICMgaGVpZ2h0IGF0IHdoaWNoIHdpbmQgaXMgbWVhc3VyZWQKICB3aW5kIDwtIHdpbmQqKDQuODcvbG9nKDY3Kmh3LTUuNDIpKSAgCiAgCiAgIyBzdG9tYXRhbCBjb25kdWN0YW5jZSBhZGp1c3RtZW50IGZvciBsb3cgdGVtcGVyYXR1cmVzCiAgc3I9MTAwICMgc3RvbWF0YWwgcmVzaXN0YW5jZSBzZWMvbQoJa3NfbWluPS4wMSAjIG1pbmltdW0gdmFsdWUgZm9yIHRlbXBzIGJlbG93IFQxCglUbD0tMTAgICAgICAgIyBtaW5pbXVtIHRlbXAgKHNjIGdvZXMgdG8ga3NfbWluIGJlbG93IHRoaXMgdGVtcCkKCVQwPTUJCSMgb3B0aW1hbCB0ZW1wCglUaD0xMDAgICAgICMgbWF4aW11bSB0ZW1wIChzYyBnb2VzIHRvIHplcm8gYWJvdmUgdGhpcykKCXRocmVzaD01ICAgIyB0ZW1wZXJhdHVyZSB0aHJlc2hvbGQgYmVsb3cgd2hpY2ggdG8gYXBwbHkgSmFydmlzIGVxdWF0aW9uIChrcz0xIGFib3ZlIHRoaXMgdGVtcCkKCWI0PShUaC1UMCkvKFRoLVRsKSAgIyBmcm9tIEphcnZpcyAxOTc4CgliMz0xLygoVDAtVGwpKihUaC1UMCleYjQpCglrcz1wbWF4KHBtaW4oYjMqKHRtZWFuLVRsKSooVGgtdG1lYW4pXmI0LDEpLGtzX21pbikKCWtzW2lzLm5hKGtzKV0gPC0ga3NfbWluCglrc1t0bWVhbj49dGhyZXNoXSA8LSAxCgkJCgkjIGNvbnZlcnQgdG8gc3RvbWF0YWwgcmVzaXN0YW5jZS4KCXNyIDwtIHNyL2tzCgkJCgkjIHJhIGlzIGFlcm9keW5hbWljIHJlc2lzdGFuY2UsIHJzIGlzIGJ1bGsgc3VyZmFjZSByZXNpc3RhbmNlCglyYSA8LSAyMDgvd2luZCAjIAoJcnMgPC0gc3IvKDAuNSoyNCowLjEyKSAjIHZhbHVlIG9mIDcwIHdoZW4gc3I9MTAwCgkKCSMgU2F0dXJhdGlvbiB2YXBvciBwcmVzc3VyZSAsIAoJZXMgPC0gMC42MTA4KmV4cCh0bWluKjE3LjI3Lyh0bWluKzIzNy4zKSkvMiswLjYxMDgqZXhwKHRtYXgqMTcuMjcvKHRtYXgrMjM3LjMpKS8yICAjIHNhdHVyYXRpb24gdmFwb3IgcHJlc3N1cmUKCWVhIDwtIDAuNjEwOCpleHAoKGRwdCkqMTcuMjcvKChkcHQpKzIzNy4zKSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBhY3R1YWwgdmFwb3IgcHJlc3N1cmUKCXZwZCA8LSBlcyAtIGVhCgl2cGRbdnBkPDBdIDwtIDAgICAgCiAgCgkjIGRlbHRhIC0gU2xvcGUgb2YgdGhlIHNhdHVyYXRpb24gdmFwb3IgcHJlc3N1cmUgdnMuIGFpciB0ZW1wZXJhdHVyZSBjdXJ2ZQoJZGVsdGEgPC0gKDQwOTggKiBlcykvKHRtZWFuICsgMjM3LjMpXjIgIAoJUCA8LSAxMDEuMyooKDI5My0wLjAwNjUqZWxldikvMjkzKV41LjI2ICAjIEJhcm9tZXRyaWMgcHJlc3N1cmUgaW4ga1BhCglsYW1iZGEgPC0gMi41MDEtMi4zNjFlLTMqdG1lYW4gIyBsYXRlbnQgaGVhdCBvZiB2YXBvcml6YXRpb24gICAgCgljcCA8LSAxLjAxMyoxMF4tMyAjIHNwZWNpZmljIGhlYXQgb2YgYWlyCglnYW1tYSA8LSBjcCpQLygwLjYyMipsYW1iZGEpICMgUHN5Y2hyb21ldGVyIGNvbnN0YW50IChrUGEgQy0xKQoJcGEgPC0gUC8oMS4wMSoodG1lYW4rMjczKSowLjI4NykgIyBtZWFuIGFpciBkZW5zaXR5IGF0IGNvbnN0YW50IHByZXNzdXJlCgkKCSMgQ2FsY3VsYXRlIHBvdGVudGlhbCBtYXggc29sYXIgcmFkaWF0aW9uIG9yIGNsZWFyIHNreSByYWRpYXRpb24uCglHU0MgPSAwLjA4MiAgICAgICMgTUogbSAtMiBtaW4tMSAoc29sYXIgY29uc3RhbnQpCglwaGkgPC0gcGkqbGF0LzE4MCAKCWRyIDwtIDErMC4wMzMqY29zKDIqcGkvMzY1KmRveSkgICAgICAKCWRlbHQgPC0gMC40MDkqc2luKDIqcGkvMzY1KmRveS0xLjM5KSAgICAgCglvbWVnYXMgPC0gYWNvcygtdGFuKHBoaSkqdGFuKGRlbHQpKSAgICAgCglSYSA8LSAyNCo2MC9waSpHU0MqZHIqKG9tZWdhcypzaW4ocGhpKSpzaW4oZGVsdCkgK2NvcyhwaGkpKmNvcyhkZWx0KSpzaW4ob21lZ2FzKSkgIyBkYWlseSBleHRyYXRlcnJlc3RyaWFsIHJhZGlhdGlvbgoJUnNvIDwtIFJhKigwLjc1KzJlLTUqZWxldikgICAgICNGb3IgYSBjbG91ZGxlc3MgZGF5LCBScyBpcyByb3VnaGx5IDc1JSBvZiBleHRyYXRlcnJlc3RyaWFsIHJhZGlhdGlvbiAoUmEpCgoJIyByYWRmcmFjdGlvbiBpcyBhIG1lYXN1cmUgb2YgcmVsYXRpdmUgc2hvcnR3YXZlIHJhZGlhdGlvbiwgb3Igb2YKCSMgcG9zc2libGUgcmFkaWF0aW9uIChjbG91ZHkgdnMuIGNsZWFyLXNreSksIG5lZWRzIHRvIGJlIGxlc3MgdGhhbiAxCglyYWRmcmFjdGlvbiA8LSByYWRpYXRpb24vUnNvCglyYWRmcmFjdGlvbltyYWRmcmFjdGlvbj4xXSA8LSAxCgkKCSMgbG9uZ3dhdmUgcmFkaWF0aW9uCglsb25ndyA8LSA0LjkwM2UtOSpuX2RheXMqKCh0bWF4KzI3My4xNSleNCsodG1pbisyNzMuMTUpXjQpLzIqKC4zNC0uMTQqc3FydChlYSkpKigxLjM1KnJhZGZyYWN0aW9uLS4zNSkgICAgIAoJCiAgIyBuZXQgcmFkaWF0aW9uCgluZXRyYWQgPC0gcmFkaWF0aW9uKm5fZGF5cyooMS1hbGJlZG8pLWxvbmd3ICAgICAKCQoJIyBFVCBmcm9tIGxvbmctZm9ybSBQLU0gZXFuLgoJZXQwIDwtIC40MDgqKChkZWx0YSoobmV0cmFkLUcpKSsocGEqY3AqdnBkL3JhKjM2MDAqMjQqbl9kYXlzKSkvKGRlbHRhK2dhbW1hKigxK3JzL3JhKSkKCXJldHVybihldDApCn0gCmBgYAoKRGFuIGFsc28gbWFkZSBhIG5ldyB2ZXJzaW9uIG9mIHRoZSBkYWlseSBFVDAgZnVuY3Rpb24uIEFkZCBpdCBhbHNvIHNvIHdlIGNhbiBjb21wYXJlIHRoZSByZXN1bHRzLiBVU0UgVEhJUyBPTkUgSU5TVEVBRCEhCmBgYHtyfQpkYWlseUVUMCA8LSBmdW5jdGlvbihsYXQsZWxldixwc3VuLHdpbmQsZG95LHRtYXgsdG1pbixyaCl7CgkjIFRoaXMgaXMgYSBFVDAgZnVuY3Rpb24gZGVzaWduZWQgZm9yIGRhaWx5IGlucHV0cy4gIAogIAogICMgQXJndW1lbnRzOgogICMgcHN1bjogdmVjdG9yIG9mIGRhaWx5IHBlcmNlbnQgc3Vuc2hpbmUKICAjIHRtYXgsIHRtaW46IHZlY3RvcnMgb2YgbW9udGhseSBhdmVyYWdlIG1heGltdW0gYW5kIG1pbmltdW0gdGVtcGVyYXR1cmVzIGluIEMsIAogICMgd2luZDogdmVjdG9yIG9mIG1vbnRobHkgYXZlcmFnZSB3aW5kIHNwZWVkIGluIG0vcywgCiAgIyB0bWVhbl9wcmV2OiB2ZWN0b3Igb2YgbWVhbiB0ZW1wIGZvciB0aGUgcHJldmlvdXMgbW9udGgsIAogICMgbGF0OiB2ZWN0b3Igb2YgbGF0aXR1ZGUgaW4gZGVncmVlcyAKICAjIGVsZXY6IHZlY3RvciBvZiBlbGV2YXRpb24gaW4gbWV0ZXJzLCAKICAjIHJoOiB2ZWN0b3Igb2YgcmVsYXRpdmUgaHVtaWRpdHkgKG1lYW4gZGFpbHkpLgogICMgdG1lYW5fcHJldjogdmVjdG9yIG9mIG1lYW4gdGVtcCBvZiBwcmV2aW91cyBtb250aCBpbiBDCiAgIyBhbGJlZG86IHNjYWxlciBvciB2ZWN0b3Igb2YgYWxiZWRvIHZhbHVlcywgCiAgIyBkb3k6IHNjYWxhciBkYXkgb2YgeWVhciAxLTM2NSwKICAjIFZhbHVlOiAKICAjIFJldHVybnMgYSB2ZWN0b3Igb2YgRVQwIHZhbHVlcy4KICAKI3NvbWUgY29uc3RhbnRzCm5fZGF5cyA9IDEKR1NDID0gMC4wODIgICAgICAjIE1KL20yL21pbiAoc29sYXIgY29uc3RhbnQpCmFsYmVkbyA9IDAuMjMgIyhoeXBvdGhldGljYWwgZ3Jhc3MgcmVmZXJlbmNlIGNyb3ApCkcgPC0gMCAjIGFzc3VtZSBzb2lsIGhlYXQgZmx1eCB0byBiZSB6ZXJvCmh3PTUgIyBoZWlnaHQgYXQgd2hpY2ggd2luZCBpcyBtZWFzdXJlZApjcCA8LSAxLjAxMyoxMF4tMyAjIHNwZWNpZmljIGhlYXQgb2YgYWlyCgojIFN0ZXAgMQkKICB0bWVhbiA8LSAodG1pbit0bWF4KS8yCiAgbGFtYmRhIDwtIDIuNTAxLTIuMzYxZS0zKnRtZWFuICMgbGF0ZW50IGhlYXQgb2YgdmFwb3JpemF0aW9uICAgIAoKIyBTdGVwIDI6IE1lYW4gZGFpbHkgc29sYXIgcmFkaWF0aW9uIChScyBpbiB1bml0cyBNSi9tMi9kYXkpLiBTZWUgU3RlcCAxMi4gIE5vdGhpbmcgbmVlZGVkIGhlcmUuCgojIFN0ZXAgMzogd2luZCBhZGp1c3RtZW50IHRvIDJtIGZyb20gMTBtIG1lYXN1cmVtZW50cwogIHdpbmQgPC0gd2luZCooNC44Ny9sb2coNjcqaHctNS40MikpICAKICAKIyBTdGVwIDEwOiBNZWFuIHNhdHVyYXRpb24gdmFwb3IgcHJlc3N1cmUgKGVzKQoJZXMgPC0gMC42MTA4KmV4cCh0bWluKjE3LjI3Lyh0bWluKzIzNy4zKSkvMiswLjYxMDgqZXhwKHRtYXgqMTcuMjcvKHRtYXgrMjM3LjMpKS8yICAjIHNhdHVyYXRpb24gdmFwb3IgcHJlc3N1cmUKIyBTdGVwIDQ6IFNsb3BlIG9mIHRoZSBzYXR1cmF0aW9uIHZhcG9yIHByZXNzdXJlIHZzLiBhaXIgdGVtcGVyYXR1cmUgY3VydmUgKGRlbHRhKQogIAlkZWx0YSA8LSAoNDA5OCAqIGVzKS8odG1lYW4gKyAyMzcuMyleMiAgCgojIFN0ZXAgNTogQXRtb3NwaGVyaWMgUHJlc3N1cmUgKFApCglQIDwtIDEwMS4zKigoMjkzLTAuMDA2NSplbGV2KS8yOTMpXjUuMjYgICMgQmFyb21ldHJpYyBwcmVzc3VyZSBpbiBrUGEKCiMgU3RlcCA2OiBQc3ljaHJvbWV0cmljIGNvbnN0YW50IChnYW1tYSkKCWdtbWEgPC0gY3AqUC8oMC42MjIqbGFtYmRhKSAjIFBzeWNocm9tZXRlciBjb25zdGFudCAoa1BhIEMtMSkKCiNTdGVwIDc6IERlbHRhIFRlcm0gKGR0KSAoYXV4aWxpYXJ5IGNhbGN1bGF0aW9uIGZvciBSYWRpYXRpb24gVGVybSkKCWR0IDwtIGRlbHRhLyhkZWx0YStnbW1hKigxKzAuMzQqd2luZCkpCgojIFN0ZXAgODogUHNpIFRlcm0gKHB0KSBhdXhpbGlhcnkgY2FsY3VsYXRpb24gZm9yIFdpbmQgVGVybS4KCXB0IDwtIGdtbWEvKGRlbHRhK2dtbWEqKDErMC4zNCp3aW5kKSkKCQojIFN0ZXAgOTogVGVtcGVyYXR1cmUgVGVybSAoYXV4aWxpYXJ5IGNhbGN1bGF0aW9uIGZvciBXaW5kIFRlcm0pCgl0dCA8LSAoOTAwLyh0bWVhbisyNzMpKSp3aW5kCgojIFN0ZXAgMTE6IGFjdHVhbCB2YXBvciBwcmVzc3VyZSAoZWEpCgllYSA8LSAocmgvMTAwKSooMC42MTA4KmV4cCh0bWluKjE3LjI3Lyh0bWluKzIzNy4zKSkrMC42MTA4KmV4cCh0bWF4KjE3LjI3Lyh0bWF4KzIzNy4zKSkpLzIKCXZwZCA8LSBlcyAtIGVhCgl2cGRbdnBkPDBdIDwtIDAgICAgCgojIFN0ZXAgMTI6IFRoZSBpbnZlcnNlIHJlbGF0aXZlIGRpc3RhbmNlIEVhcnRoLVN1biAoZHIpIGFuZCBzb2xhciBkZWNsaW5hdGlvbiAoZGVsdCkKCSMgQ2FsY3VsYXRlIHBvdGVudGlhbCBtYXggc29sYXIgcmFkaWF0aW9uIG9yIGNsZWFyIHNreSByYWRpYXRpb24uCglkciA8LSAxKzAuMDMzKmNvcygyKnBpLzM2NSpkb3kpICAgICAgCglkZWx0IDwtIDAuNDA5KnNpbigyKnBpLzM2NSpkb3ktMS4zOSkKCiMgU3RlcCAxMzogQ29udmVydCBsYXRpdHVkZSB0byByYWRpYW5zIChwaGkpCglwaGkgPC0gcGkqbGF0LzE4MCAKCiMgU3RlcCAxNDogU3Vuc2V0IGhvdXIgYW5nbGUgKHdzKQoJb21lZ2FzIDwtIGFjb3MoLXRhbihwaGkpKnRhbihkZWx0KSkKCiMgU3RlcCAxNTogRGFpbHkgZXh0cmF0ZXJyZXN0cmlhbCByYWRpYXRpb24gKFJhKQoJUmEgPC0gMjQqNjAvcGkqR1NDKmRyKihvbWVnYXMqc2luKHBoaSkqc2luKGRlbHQpICtjb3MocGhpKSpjb3MoZGVsdCkqc2luKG9tZWdhcykpCgojIFN0ZXAgMTY6IENsZWFyIHNreSBzb2xhciByYWRpYXRpb24gKFJzbykuIFJzIGlzIGZyYWN0aW9uIG9mIFJzbwoJUnNvIDwtIFJhKigwLjc1KzJlLTUqZWxldikgICAgICNGb3IgYSBjbG91ZGxlc3MgZGF5LCBScyBpcyByb3VnaGx5IDc1JSBvZiBleHRyYXRlcnJlc3RyaWFsIHJhZGlhdGlvbiAoUmEpCgkjIHJhZGZyYWN0aW9uIGlzIGEgbWVhc3VyZSBvZiByZWxhdGl2ZSBzaG9ydHdhdmUgcmFkaWF0aW9uLCBuZWVkcyB0byBiZSBsZXNzIHRoYW4gMQoJcmFkZnJhY3Rpb24gPC0gcHN1bgoJcmFkZnJhY3Rpb25bcmFkZnJhY3Rpb24+MV0gPC0gMQoJUnM8LVJzbypyYWRmcmFjdGlvbgoKIyBTdGVwIDE3OiBOZXQgc29sYXIgb3IgbmV0IHNob3J0d2F2ZSByYWRpYXRpb24gKFJucykuICAKCVJucyAgPC0gKDEtYWxiZWRvKSpScwoKIyBTdGVwIDE4OiBOZXQgb3V0Z29pbmcgbG9uZ3dhdmUgc29sYXIgcmFkaWF0aW9uIChSbmwpCglSbmwgPC0gNC45MDNlLTkqbl9kYXlzKigodG1heCsyNzMuMTUpXjQrKHRtaW4rMjczLjE1KV40KS8yKiguMzQtLjE0KnNxcnQoZWEpKSooMS4zNSpyYWRmcmFjdGlvbi0uMzUpICAgICAKCiMgU3RlcCAxOTogTmV0IHJhZGlhdGlvbiAoUm4pCglSbiA8LSBSbnMqbl9kYXlzLVJubCAgICAgCgojIEZpbmFsIHN0ZXAKIyBSYWRpYXRpb24gdGVybS4gICgwLjQwOCpSbikgaXMgbmV0IHJhZGlhdGlvbiBleHByZXNzZWQgaW4gZXF1aXZhbGVudCBvZiBldmFwb3JhdGlvbiAobW0pCQoJRVRyYWQgPC0gZHQqMC40MDgqUm4KIyBXaW5kIHRlcm0gKEVUd2luZCkJCglFVHdpbmQgPC0gcHQgKiB0dCooZXMtZWEpCgpldDAgPC0gRVR3aW5kICsgRVRyYWQKCnJldHVybihkYXRhLmZyYW1lKEVUd2luZD1FVHdpbmQsRVRyYWQ9RVRyYWQsZXQwPWV0MCkpCn0KYGBgCgpBbmQgdG8gY2FsY3VsYXRlIHRoZSBzb2lsIHdhdGVyIGRlZmljaXQsIHdlIG5lZWQgdG8gdXNlIHRoZSBmb3VydGggZXF1YXRpb24gZnJvbSBEb2Jyb3dza2kgZXQgYWwuIFdlIGFscmVhZHkgaGF2ZSB0aGUgcmVmZXJlbmNlIGV2YXBvdHJhbnNwaXJhdGlvbiwgY2FsY3VsYXRlZCBpbiB0aGUgcHJldmlvdXMgZnVuY3Rpb24uIFdlIGFsc28gaGF2ZSB0aGUgbW9udGhseSB3YXRlciBpbnB1dCwgYW5kIGl0J3MgYWxyZWFkeSBpbiB0aGUgY29ycmVjdCB1bml0cyAobW0pOiBwcmVjaXBpYXRpb24uIFRoZSBmdW5jdGlvbiBjYWxscyBmb3Igc29pbCB3YXRlciBjb250ZW50IGZyb20gdGhlIHByZXZpb3VzIG1vbnRoIChkYXkpLCBidXQgaXQgd2lsbCBzdGlsbCBmdW5jdGlvbiBpZiBpdCBpcyBsZWZ0IG91dC4KYGBge3J9CmFldG1vZF9Eb2Jyb3dza2kgPC0gZnVuY3Rpb24oZXQwLGlucHV0LGF3Yyxzb2lsX3ByZXY9TlVMTCl7CiAgIyBUaGlzIGZ1bmN0aW9uIGNvbXB1dGVzIEFFVCBnaXZlbiBFVDAsIEgyTyBpbnB1dCwgc29pbCB3YXRlciBjYXBhY2l0eSwgYW5kIGJlZ2lubmluZy1vZi1tb250aCBzb2lsIG1vaXN0dXJlCiAgIyBBcmd1bWVudHM6CiAgIyBldDA6IHZlY3RvciBvZiBtb250aGx5IHJlZmVyZW5jZSBldmFwb3RyYW5zcGlyYXRpb24gaW4gbW0KICAjIGlucHV0OiB2ZWN0b3Igb2YgbW9udGhseSB3YXRlciBpbnB1dCB0byBzb2lsIGluIG1tCiAgIyBhd2M6IHZlY3RvciBvZiBzb2lsIHdhdGVyIGNhcGFjaXR5IGluIG1tCiAgIyBzb2lsX3ByZXY6IHZlY3RvciBvZiBzb2lsIHdhdGVyIGNvbnRlbnQgZm9yIHRoZSBwcmV2aW91cyBtb250aCAobW0pLiAgSWYgbGVmdCBOVUxMIHRoaXMgaXMgYXNzaWduZWQgdG8gYmUgemVyby4KICAjCiAgIyBWYWx1ZToKICAjIHJldHVybnMgYSBkYXRhIGZyYW1lIHdpdGggY29sdW1ucyBmb3IgQUVULCBkZWZpY2l0LCBlbmQtb2YtbW9udGggc29pbCBtb2lzdHVyZSwgYW5kIHJ1bm9mZi4KICAKICBOIDwtIGxlbmd0aChldDApCiAgcnVub2ZmIDwtIGRlZiA8LSAgYWV0IDwtIHNvaWwgPC0gcmVwKE5BLE4pICMgCiAgaWYoaXMubnVsbChzb2lsX3ByZXYpKSBzb2lsX3ByZXYgPC0gcmVwKDAsTikKICAKICBkZWx0YXNvaWwgPC0gaW5wdXQtZXQwICMgcG9zaXRpdmU9ZXhjZXNzIEgyTywgbmVnYXRpdmU9SDJPIGRlZmljaXQKICAKICAjIENhc2Ugd2hlbiB0aGVyZSBpcyBhIG1vaXN0dXJlIHN1cnBsdXM6CiAgICBDYXNlIDwtIGRlbHRhc29pbD49MAogIGlmKHN1bShDYXNlKT4wKXsKICAgIGFldFtDYXNlXSA8LSBldDBbQ2FzZV0KICAgIGRlZltDYXNlXSA8LSAwCiAgICBzb2lsW0Nhc2VdIDwtIHBtaW4oc29pbF9wcmV2W0Nhc2VdK2RlbHRhc29pbFtDYXNlXSxhd2NbQ2FzZV0pCSMgaW5jcmVtZW50IHNvaWwgbW9pc3R1cmUsIGJ1dCBub3QgYWJvdmUgd2F0ZXIgaG9sZGluZyBjYXBhY2l0eQogICAgcnVub2ZmW0Nhc2VdIDwtIHBtYXgoc29pbF9wcmV2W0Nhc2VdK2RlbHRhc29pbFtDYXNlXS1hd2NbQ2FzZV0sMCkgIyB3aGVuIGF3YyBpcyBleGNlZWRlZCwgc2VuZCB0aGUgcmVzdCB0byBydW5vZmYKICB9CiAgCiAgIyBDYXNlIHdoZXJlIHRoZXJlIGlzIGEgbW9pc3R1cmUgZGVmaWNpdDogIHNvaWwgbW9pc3R1cmUgaXMgcmVkdWNlZAogIENhc2UgPC0gZGVsdGFzb2lsPDAKICBpZihzdW0oQ2FzZSk+MCl7CiAgICBzb2lsZHJhd2Rvd24gPC0gc29pbF9wcmV2W0Nhc2VdKigxLWV4cCgtKGV0MC1pbnB1dClbQ2FzZV0vYXdjW0Nhc2VdKSkJIyB0aGlzIGlzIHRoZSBuZXQgY2hhbmdlIGluIHNvaWwgbW9pc3R1cmUgKG5lZykKICAgIGFldFtDYXNlXSA8LSBwbWluKGlucHV0W0Nhc2VdICsgc29pbGRyYXdkb3duLGV0MFtDYXNlXSkKICAgIGRlZltDYXNlXSA8LSBldDBbQ2FzZV0gLSBhZXRbQ2FzZV0KICAgIHNvaWxbQ2FzZV0gPC0gc29pbF9wcmV2W0Nhc2VdLXNvaWxkcmF3ZG93bgogICAgcnVub2ZmW0Nhc2VdIDwtIDAKICB9CiAgCiAgcmV0dXJuKGRhdGEuZnJhbWUoYWV0PWFldCxkZWY9ZGVmLHNvaWw9c29pbCxydW5vZmY9cnVub2ZmKSkKICAKfQpgYGAKCk5vdGU6IGNvdWxkbid0IGdldCB0aGUgY29kZSBmcm9tIERvYnJvd3NraSBldCBhbC4gdG8gd29yaywgc28gaGVyZSdzIGFub3RoZXIgYWN0dWFsIGV2YXBvdHJhc3BpcmF0aW9uIGZ1bmN0aW9uIGZyb20gRGFuLCBiYXNlZCBvbiB0aGlzIHdlYnNpdGUncyBjYWxjdWxhdG9yOiBodHRwOi8vZ2VvZy51b3JlZ29uLmVkdS9lbnZjaGFuZ2Uvc29mdHdhcmUvQUVUY2FsY3VsYXRvci5wZGYuIFVTRSBUSElTIE9ORS4gCmBgYHtyfQphZXRtb2QgPC0gZnVuY3Rpb24oZXQwLHByZWNpcCxhd2MpewogICMgVGhpcyBmdW5jdGlvbiBjb21wdXRlcyBBRVQgZ2l2ZW4gRVQwLCBIMk8gaW5wdXQsIHNvaWwgd2F0ZXIgY2FwYWNpdHksIGFuZCBiZWdpbm5pbmctb2YtbW9udGggc29pbCBtb2lzdHVyZQogICMgQXJndW1lbnRzOgogICMgZXQwOiB2ZWN0b3Igb2YgbW9udGhseSByZWZlcmVuY2UgZXZhcG90cmFuc3BpcmF0aW9uIGluIG1tCiAgIyBpbnB1dDogdmVjdG9yIG9mIG1vbnRobHkgd2F0ZXIgaW5wdXQgdG8gc29pbCBpbiBtbQogICMgYXdjOiBzaW5nbGUgdmFsdWUgZm9yIHNvaWwgd2F0ZXIgY2FwYWNpdHkgaW4gbW0KICAjCiAgIyBWYWx1ZToKICAjIHJldHVybnMgYSBkYXRhIGZyYW1lIHdpdGggY29sdW1ucyBmb3IgQUVULCBkZWZpY2l0LCBzb2lsIHdhdGVyIGNvbnRlbnQsIGFuZCBydW5vZmYuCiAgCiAgTiA8LSBsZW5ndGgoZXQwKQogIHJ1bm9mZiA8LSBkZWYgPC0gYWV0IDwtIHNvaWwgPC0gcmVwKE5BLE4pIAogIHcucHJldmlvdXMgPC0gYXdjICAjc3RhcnQgd2l0aCBhdCBjYXBhY2l0eQogIAogIGZvcihpIGluIDE6Til7CiAgCWJldGEgPC0gKHcucHJldmlvdXMvYXdjKS8uNyAgIyMjICBiZXRhIGZ1bmN0aW9uIChkZWNsaW5pbmcgYXZhaWxhYmlsaXR5IGZ1bmN0aW9uKQoJaWYoYmV0YT4xKXsKCQliZXRhIDwtIDEKCQl9IGVsc2UgewoJCWJldGEgPC0gMS1leHAoLTYuNjgqdy5wcmV2aW91cy9hd2MpCgkJfQoJaWYoYmV0YTwwKXsgYmV0YSA8LTAgfQogIAlEZCA8LSBwcmVjaXBbaV0tZXQwW2ldICMgcG9zaXRpdmU9ZXhjZXNzIEgyTywgbmVnYXRpdmU9SDJPIGRlZmljaXQKICAJaWYoIWlzLm5hKERkKSl7CiAgCQlpZihEZDwwKXsgIyBwcmVjaXAgbGVzcyB0aGFuIGRlbWFuZCwgbG93ZXJlZCBzb2lsIHdhdGVyLCBubyBydW5vZmYKCQkJc29pbFtpXSA8LSB3LnByZXZpb3VzK2JldGEqRGQgICMjIyBzb2lsIG1vaXN0dXJlIGF0IGVuZCBvZiBkYXkgaQoJCQlydW5vZmZbaV0gPC0gMAoJCQlhZXRbaV0gPC0gcHJlY2lwW2ldKyh3LnByZXZpb3VzLXNvaWxbaV0pICAjIGFldD1wcmVjaXArbG93ZXJlZCBzb2lsIHdhdGVyCgkJCWRlZltpXSA8LSBldDBbaV0tYWV0W2ldCgkJCX0KCQlpZihEZD4wKXsgICMgcHJlY2lwIG1vcmUgdGhhbiBkZW1hbmQsIGluY3JlYXNlZCBzb2lsIHdhdGVyLCBwb3NzaWJseSBydW5vZmYKCQkJc29pbFtpXSA8LSB3LnByZXZpb3VzK0RkICAjIyMgc29pbCBtb2lzdHVyZSBhdCBlbmQgb2YgZGF5IGkKCQkJaWYoc29pbFtpXT5hd2MpewoJCQkJcnVub2ZmW2ldIDwtIHNvaWxbaV0tYXdjCgkJCQlzb2lsW2ldIDwtIGF3YwoJCQkJfSBlbHNlIHsKCQkJCXJ1bm9mZltpXSA8LSAwCgkJCQl9CgkJCWFldFtpXSA8LSBldDBbaV0KCQkJZGVmW2ldIDwtIDAKCQkJfQkJCgkJdy5wcmV2aW91cyA8LSBzb2lsW2ldCgkJfSAjZW5kaWYKCX0gIyBuZXh0IGRheQoJcmV0dXJuKGRhdGEuZnJhbWUoYWV0PWFldCxkZWY9ZGVmLHJ1bm9mZj1ydW5vZmYsc29pbD1zb2lsKSkKICB9CmBgYAoKVGhpcyBqdXN0IGxlYXZlcyBzb2lsIHdhdGVyIGNhcGFjaXR5IGFzIHRoZSBsYXN0IHRoaW5nIHRoYXQgbmVlZHMgdG8gYmUgZmlndXJlZCBvdXQgKHNvaWwgd2F0ZXIgY2FwYWNpdHkgPSBkaWZmZXJlbmNlIGJldHdlZW4gZmllbGQgY2FwYWNpdHkgYW5kIHBlcm1hbmVudCB3aWx0aW5nIHBvaW50KS4gU29pbCBpbiB0aGUgS2V0YXBhbmcgYXJlYSBpcywgYWNjb3JkaW5nIHRvIHRoZSBGQU8tVU5FU0NPIFNvaWxzIE1hcDogSkQ5IDIvM2EgLS0+IER5c3RyaWMgRmx1dmlzb2xzLCBtZWRpdW0vZmluZSwgbGV2ZWwgdG8gdW5kdWxhdGluZwoKIlVzZTogRHlzdHJpYyBGbHV2aXNvbHMgb2NjdXIgbWFpbmx5IGluIFN1bWF0cmEsIEphdmEsIEthbGltYW50YW4sIElyaWFuIEpheWEsIFN1bGF3ZXNpIGFuZCB0aGUgTW9sdWNjYXMuIFRoZXkgYXJlIGRldmVsb3BlZCBvbiByZWNlbnQgZmx1dmlhdGlsZSwgbWFyaW5lIGFuZCBsYWN1c3RyaW5lIGRlcG9zaXRzIGFuZCBvY2N1cHkgcG9zaXRpb25zIG9uIHJpdmVyIGxldmVlcywgcHJlc2VudCBmbG9vZCBwbGFpbnMsIGRlbHRhcyBhbmQgbGFrZSBzaG9yZXMgd2hlcmUgYWxsdXZpdW0gaXMgZGVyaXZlZCBmcm9tIHByZWRvbWluYW50bHkgYWNpZCBwYXJlbnQgcm9ja3MuIFRoZWlyIG1hY3JvcmVsaWVmIGlzIGdlbmVyYWxseSBmbGF0LCBhbHRob3VnaCBsZXZlZXMgb2Z0ZW4gaGF2ZSBhbiB1bmR1bGF0aW5nIG1pY3JvcmVsaWVmLiBTb2lscyBzdWJqZWN0IHRvIGRlZXAgZmxvb2RpbmcgcmVtYWluIHVuZGVyIG1peGVkIHN3YW1wIGZvcmVzdC4gQWxvbmcgY29hc3RzLCBzb2lscyB3aGljaCBhcmUgcGVyaW9kaWNhbGx5IGludW5kYXRlZCBieSBzZWEgd2F0ZXIgYXJlIG1haW5seSB1bmRlciBtYW5ncm92ZSB2ZWdldGF0aW9uLCB3aGljaCBpcyBpbnRlbnNpdmVseSB1c2VkIGluIHBsYWNlcyBmb3IgY2hhcmNvYWwgbWFraW5nLiBXaGVyZSBmbG9vZGluZyBpcyBub3QgdG9vIGRlZXAsIG9yIHdoZXJlIGZsb29kIHByb3RlY3Rpb24gYW5kIGRyYWluYWdlIHdvcmtzIGhhdmUgYmVlbiBjb25zdHJ1Y3RlZCwgdGhlc2Ugc29pbHMgYXJlIGludGVuc2l2ZWx5IHVzZWQgZm9yIHBhZGR5IGN1bHRpdmF0aW9uLiBMZXZlZSBzb2lscyBhcmUgdHJhZGl0aW9uYWxseSB1c2VkIGZvciBzZXR0bGVtZW50IHNpdGVzIHdpdGggaG9tZSBnYXJkZW5zLCBvcmNoYXJkcyBhbmQgYmFuYW5hIHBsYW50YXRpb25zLiBUaGUgYmV0dGVyLWRyYWluZWQgRHlzdHJpYyBGbHV2aXNvbHMgb2Ygbm9ydGhlcm4gU3VtYXRyYSBwcm9kdWNlIERlbGl3cmFwcGVyIHRvYmFjY28sIGFuZCBydWJiZXIgYW5kIG9pbCBwYWxtIGdpdmUgdmVyeSBoaWdoIHlpZWxkcy4KClN1aXRhYmlsaXR5LiBNb3N0IER5c3RyaWMgRmx1dmlzb2xzIGFyZSBzdHJhdGlmaWVkLCBtZWRpdW0gdG8gZmluZSB0ZXh0dXJlZCwgYW5kIHBvb3JseSB0byB2ZXJ5IHBvb3JseSBkcmFpbmVkLCBhbHRob3VnaCBiZXR0ZXItZHJhaW5lZCBzb2lscyBvY2N1ciBvbiBsZXZlZXMuIFNvaWwgcmVhY3Rpb24gaXMgc2xpZ2h0bHkgdG8gc3Ryb25nbHkgYWNpZC4gT3JnYW5pYyBtYXR0ZXIgY29udGVudCBpcyB2YXJpYWJsZSwgYnV0IGdlbmVyYWxseSBtb2RlcmF0ZXRvIGhpZ2guIFRoZXNlIHNvaWxzIGFyZSBsb3cgaW4gYmFzZXMsIGJ1dCByZXNwb25kIHdlbGwgdG8gbW9kZXJhdGUgYXBwbGljYXRpb25zIG9mIG5pdHJvZ2VuIGFuZCBwaG9zcGhhdGUgZmVydGlsaXplcnMuIFdhdGVyIGNvbnRyb2wgcHJlc2VudHMgdGhlIG1haW4gcHJvYmxlbSBmb3IgdGhlIHN1Y2Nlc3NmdWwgdXRpbGl6YXRpb24gb2YgdGhlc2Ugc29pbHMuIEEgY29tYmluYXRpb24gb2YgaXJyaWdhdGlvbiBkdXJpbmcgdGhlIGRyeSBzZWFzb24gYW5kIGZsb29kIHByb3RlY3Rpb24gYW5kIGRyYWluYWdlIGR1cmluZyB0aGUgcmFpbnkgc2Vhc29uIGlzIHJlcXVpcmVkIG9uIHBvb3JseSBkcmFpbmVkIHNvaWxzLiBXaGVyZSB0aGlzIGlzIGZlYXNpYmxlLCB0d28gY3JvcHMgb2YgcmljZSBhIHllYXIgY2FuIGJlIGFjaGlldmVkLCBnaXZpbmcgaGlnaCB5aWVsZHMgdW5kZXIgZ29vZCBtYW5hZ2VtZW50IGludm9sdmluZyB0aGUgdXNlIG9mIGhpZ2gteWllbGRpbmcgdmFyaWV0aWVzLCByZWd1bGFyIGZlcnRpbGl6ZXIgYXBwbGljYXRpb24sIGFuZCB0aGUgdXNlIG9mIGluc2VjdGljaWRlcyBhbmQgcGVzdGljaWRlcy4gSW4gZHJpZXIgYXJlYXMgd2hlcmUgaXJyaWdhdGlvbiB3YXRlciBpcyBsaW1pdGVkLCByYWluZmVkIHJpY2UgZm9sbG93ZWQgYnkgYSBzdW1tZXIgbGVndW1lIGNyb3Agb3IgdG9iYWNjbyBnaXZlcyBzYXRpc2ZhY3RvcnkgcmVzdWx0cyB1bmRlciBnb29kIG1hbmFnZW1lbnQuIFJ1YmJlciwgb2lsIHBhbG0gYW5kIHN1Z2FyIGNhbmUgdGhyaXZlIHdoZXJlIGRyYWluYWdlIGlzIGFkZXF1YXRlLiBXaGVyZSBubyBkcmFpbmFnZSBvciBmbG9vZCBjb250cm9sIGlzIHJlcXVpcmVkLCB0aGVzZSBzb2lscyBtYXkgYmUgY3VsdGl2YXRlZCB1bmRlciBhIHdpZGUgcmFuZ2Ugb2YgZm9vZCwgZnJ1aXQgYW5kIGluZHVzdHJpYWwgY3JvcHMgd2l0aCBtb2RlcmF0ZSBhcHBsaWNhdGlvbnMgb2YgZmVydGlsaXplcnMuIFdoZXJlIGZsb29kIGNvbnRyb2wgaXMgbm90IGZlYXNpYmxlLCByZWNlbnQgZGV2ZWxvcG1lbnRzIGluIHRoZSBicmVlZGluZyBvZiBmbG9hdGluZyByaWNlIHZhcmlldGllcyBzZWVtIHByb21pc2luZy4gVGhlIGJldHRlci1kcmFpbmVkIGxldmVlIHNvaWxzIGFyZSBnZW5lcmFsbHkgZnVsbHkgdXRpbGl6ZWQgZm9yIHNldHRsZW1lbnQgc2l0ZXMsIGhvbWUgZ2FyZGVucywgb3JjaGFyZHMgYW5kIGJhbmFuYSBwbGFudGF0aW9ucy4iIAotLUZBTyBVTkVTQ08gU29pbCBNYXAgb2YgdGhlIFdvcmxkCgoKQWxzbywgc29tZSB1c2VmdWwgbm90ZXMgb24gZXZhcG90cmFuc3BpcmF0aW9uIGZyb20gdGhlIEZBTzogaHR0cDovL3d3dy5mYW8ub3JnL2RvY3JlcC94MDQ5MGUveDA0OTBlMDQuaHRtXV0KCiMjIFJlYWQgaW4gbGlicmFyaWVzIGFuZCB0aGUgZGF0YTsgaW5pdGlhbCBkYXRhIGV4cGxvcmF0aW9uCgpMb2FkIGxpYnJhcmllcyBhbmQgY3JlYXRlIGEgbWluaS1mdW5jdGlvbiBmb3IgcmVhZGluZyBpbiBkYXRlcy4gCmBgYHtyfQojIGxvYWQgbGlicmFyaWVzCiMgaW5zdGFsbCB0aGUgcGFja2FnZSBpZiB5b3UgZG9uJ3QgYWxyZWFkeSBoYXZlIGl0CmlmICghcmVxdWlyZSgicGx5ciIpKSB7CiAgICAgaW5zdGFsbC5wYWNrYWdlcygicGx5ciIpCiAgICAgbGlicmFyeShwbHlyKQp9ICAgIAoKaWYgKCFyZXF1aXJlKCJ6b28iKSkgewogICAgIGluc3RhbGwucGFja2FnZXMoInpvbyIpCiAgICAgbGlicmFyeSh6b28pCn0KCiMjIGNyZWF0ZSBhIG1pbmkgZnVuY3Rpb24gdGhhdCBtYWtlcyB0aGUgZGF0ZSBvZiB0aGUgZGV0ZWN0ZWQgZmlyZSBiZSByZWFkIGluIGFzIGEgUE9TSVhjdCBkYXRlLCByYXRoZXIgdGhhbiBhcyBhIGNoYXJhY3RlciBvciBhIHN0cmluZwojIyBub3RlIHRoYXQgYmVjYXVzZSB0aGlzIGRhdGEgaXMgZnJvbSBJbmRvbmVzaWEsIHRoZSBkYXkgY29tZXMgYmVmb3JlIHRoZSBtb250aApzZXRDbGFzcygnZGRtbXl5eXlfS1RHJykKc2V0QXMoImNoYXJhY3RlciIsImRkbW15eXl5X0tURyIsIGZ1bmN0aW9uKGZyb20pIGFzLkRhdGUoZnJvbSwgZm9ybWF0PSIlZC8lbS8lWSIsdHo9IlVUQys3IikpICAgICMgYWxzbyBzZXRzIHRoZSB0aW1lIHpvbmU7IHJlYWxseSBqdXN0IG5lZWRlZCBzbyB3ZSBkb24ndCBnZXQgYW4gZXJyb3IgYmVjYXVzZSBhbGwgb2YgdGhpcyBkYXRhIGlzIGZyb20gdGhlIHNhbWUgc3BvdApgYGAKCldlIG5lZWQgdG8gcmVhZCBpbiB0aGUgY2xpbWF0ZSBkYXRhIGZyb20gS2V0YXBhbmcsIGFuZCBwZXJmb3JtIGEgY291cGxlIG9mIGNhbGN1bGF0aW9ucyB0byBnZXQgZXZlcnl0aGluZyBpbnRvIHRoZSByaWdodCB1bml0cyBhbmQgZm9ybWF0LiAKYGBge3J9CiMgcmVhZCBpbiB0aGUgS2V0YXBhbmcgY2xpbWF0ZSBkYXRhCiMjIG5vIGRhdGEgaXMgZ2l2ZW4gdGhlIHZhbHVlIDk5OTkgaW4gdGhlIG9yaWdpbmFsIGRhdGE7IG5hLnN0cmluZ3MgY29udmVydHMgaW5kaWNhdGVkIHZhbHVlcyB0byBOQSAod2hpY2ggaXMgd2hhdCBSIHVzZXMgdG8gaW5kaWNhdGUgbm8gZGF0YSkgd2l0aG91dCBhbnkgZXh0cmEgc3RlcHMKIyMgOTk5OSBpcyBubyBkYXRhCiMjIDg4ODggaXMgbm8gbWVhc3VyYWJsZSBkYXRhLiBub3Qgc3VyZSBob3cgdGhpcyBpcyBkaWZmZXJlbnQgdGhhbiBubyBkYXRhLi4uIG1heWJlIHRoZXNlIHNob3VsZCBhY3R1YWxseSBiZSAwPwpLVEc8LXJlYWQuY3N2KCIvVXNlcnMvbGF1cmVuaGVuZHJpY2tzL0RvY3VtZW50cy9Cb3JuZW8vS2V0YXBhbmdfQ2xpbWF0ZUZpcmUvY2xpbWF0ZV9kYXRhL0tldGFwYW5nQ2xpbWF0ZTE5ODZfMjAxNiBDT1BZLmNzdiIsaGVhZGVyPVQsc2VwPSIsIixuYS5zdHJpbmdzPWMoIjk5OTkiLCI4ODg4IiksY29sQ2xhc3NlcyA9IGMoImNoYXJhY3RlciIsImZhY3RvciIsImRkbW15eXl5X0tURyIscmVwKCJudW1lcmljIiw3KSwiZmFjdG9yIiwibnVtZXJpYyIsImZhY3RvciIpICkKCiMgcmVuYW1lIHRoZSBjb2x1bW5zIGludG8gZW5nbGlzaApjb2xuYW1lcyhLVEcpPC1jKCJzdGF0aW9uIiwic3RhdGlvbklEIiwiZGF0ZSIsIlRfbWluIiwiVF9tYXgiLCJUX2F2ZyIsImh1bWlkaXR5IiwicHJlY2lwIiwic3Vuc2hpbmUiLCJ3aW5kX3NwZWVkX2tub3RzIiwid2luZF9kaXIiLCJ3aW5kX21heGd1c3Rfa25vdHMiLCJ3aW5kX21heGd1c3RfZGlyIikKCmhlYWQoS1RHKQpgYGAKCgpXZSBhbHJlYWR5IGhhdmU6CiogTWF4aW11bSB0ZW1wZXJhdHVyZSBpbiBDZWxzaXN1czsKKiBNaW5pbXVtIHRlbXBlcmF0dXJlIGluIENlbHNpdXM7IGFuZAoqIEh1bWlkaXR5IChhc3N1bWluZyBpdCBpcyByZWxhdGl2ZSBodW1pZGl0eSkuCgpXZSBhbHNvIGhhdmU6CiogV2luZCBzcGVlZCwgYnV0IGl0IG5lZWRzIHRvIGJlIGNvbnZlcnRlZCBmcm9tIGtub3RzIHRvIG1ldGVycyBwZXIgc2Vjb25kOwoqIERhdGUsIHdoaWNoIG5lZWRzIHRvIGJlIGNvbnZlcnRlZCB0byB0aGUgZGF5IG9mIHRoZSB5ZWFyIChha2EgSnVsaWFuIGRhdGUpOyBhbmQKKiBIb3VycyBvZiBzdW5zaGluZS4gCgpXZSBuZWVkIHRvIGNhbGNsdWF0ZTogCiogRGV3cG9pbnQgKGNhbiBiZSBjYWxjdWxhdGVkIGZyb20gdGVtcGVyYXR1cmUpOyBhbmQgCiogU29sYXIgcmFkaWF0aW9uIChjYW4gYmUgY2FsY2x1YXRlZCBmcm9tIHRoZSBob3VycyBvZiBzdW5zaGluZSkuCgpXZSBjYW4gdXNlIHNvbWUgc2ltcGxpZmljYXRpb25zIHRvIGNhbGN1bGF0ZSB0aGVzZSBtb3JlIGVhc2lseTsgbmVlZCB0byBjaGVjayBhbmQgc2VlIGlmIHRoZXNlIHNpbXBsaWZpY2F0aW9ucyBhcmUgdmFsaWQuIAoKQnV0IGZpcnN0LCBzb21lIHF1aWNrIGRhdGEgZXhwbG9yYXRpb24uIApgYGB7cn0KIyBsb29rIGF0IHRoZSBzdW5zaGluZSBkYXRhICAKYm94cGxvdChLVEckc3Vuc2hpbmUsbWFpbj0iSG91cnMgb2YgU3Vuc2hpbmUiKQoKIyBsb29rIGF0IHRoZSB0ZW1wZXJhdHVyZSBkYXRhICAKYm94cGxvdChLVEckVF9taW4sbWFpbj0iTWluaW11bSBUZW1wZXJhdHVyZSIpCmJveHBsb3QoS1RHJFRfbWF4LG1haW49Ik1heGltdW0gVGVtcGVyYXR1cmUiKQpib3hwbG90KEtURyRUX2F2ZyxtYWluPSJBdmVyYWdlIFRlbXBlcmF0dXJlIikKCiMgbG9vayBhdCB0aGUgaHVtaWRpdHkgZGF0YQpib3hwbG90KEtURyRodW1pZGl0eSxtYWluPSJSZWxhdGl2ZSBIdW1pZGl0eSIpCgojIHByZWNpcCBkYXRhCmJveHBsb3QoS1RHJHByZWNpcCxtYWluPSJEYWlseSBQcmVjaXBpdGF0aW9uIikKCiMgd2luZCBkYXRhCmJveHBsb3QoS1RHJHdpbmRfc3BlZWRfa25vdHMsbWFpbj0iV2luZCBTcGVlZCIpCmBgYApMb29rcyBsaWtlIHRoZSBkYXRhIGlzIGRlZmluaXRlbHkgbm90IG5vcm1hbGx5IGRpc3RyaWJ1dGVkIGZvciBtb3N0IG9mIHRoZXNlIHZhcmlhYmxlcywgYnV0IHNpbmNlIHdlJ3JlIG5vdCB0cnlpbmcgdG8gZG8gYW55IHN0YXRpc3RpY2FsIGFuYWx5c2VzIG9uIHRoZXNlIGl0IHNob3VsZG4ndCBiZSBhIHByb2JsZW0uIFdlIGFsc28gbmVlZCB0byBub3RlIHRoYXQgdGhlcmUgYXJlIHNvbWUgY2xlYXIgcHJvYmxlbXMgaW4gdGhlIGRhdGEuIAoKIyMgTWFzc2FnaW5nIHRoZSBkYXRhIChkZWFsaW5nIHdpdGggTkFzIGFuZCBvdGhlciBub24tc2Vuc2ljYWwgdmFsdWVzKQoKVGhlcmUgYWxzbyBzZWVtIHRvIGJlIHNvbWUgaXNzdWVzIHdpdGggdGhlIHByZWNpcGl0YXRpb24gZGF0YS0tdGhlcmUgaXMgYSBMT1Qgb2YgcHJvYmFibHkgbWlzc2luZyBkYXRhLS1idXQgdGhvc2UgYXJlIGV2ZW4gaGFyZGVyIHRvIHRyYWNrIGRvd24sIHNvIGp1c3QgcmVhZCB0aGVtIGluIGFzIDAgZm9yIG5vdy4gCgpBbm90aGVyIHRoaW5nIHRoYXQgbWlnaHQgbmVlZCB0byBiZSBkZWFsdCB3aXRoIGhhcyB0byBkbyB3aXRoIHRoZSB3aW5kc3BlZWQuIFdpbmRzcGVlZCB2YXJpZXMgd2l0aCBoZWlnaHQsIHNvIHdlIG5lZWQgdG8gZmluZCBvdXQgdGhlIGhlaWdodCB0aGF0IHRoZSBtZWFzdXJlbWVudCB3YXMgdGFrZW4gYXQuIFRoZXJlIGFyZSBhbHNvIGEgbG90IG9mIG1pc3NpbmcgdmFsdWVzLiAKCkNhbGN1bGF0ZSBhIG1vdmluZyBhdmVyYWdlIHRoYXQgd2UgY2FuIHRoZW4gdXNlIHRvIGZpbGwgaW4gc29tZSBvZiB0aGUgbWlzc2luZyB2YWx1ZXMuIFRoaXMgaXNuJ3QgdGhlIGlkZWFsIGFwcHJvYWNoLCBidXQgd2UgaGF2ZSB0byBoYXZlIHNvbWUgdmFsdWUtLW90aGVyd2lzZSB0aGUgQUVUIGZvciB0aGF0IGRheSB3aWxsIGJlIG1vZGVsZWQgYXMgMCwgd2hpY2ggaXMgZXZlbiBsZXNzIHJlYWxpc3RpYyB0aGFuIGZpbGxpbmcgaXQgaW4gd2l0aCBtb2RlbGVkL3Ntb290aGVkIGRhdGEuIChOb3RlIHRoYXQgZm9yIHNvbWUgdGhpbmdzLCBsaWtlIHRlbXBlcmF0dXJlLCBhIG1vdmluZyBhdmVyYWdlIGRvZXMgc2VlbSByZWFzb25hYmxlLCBidXQgaXQgbWFrZXMgbGVzcyBzZW5zZSBmb3Igb3RoZXIgdGhpbmdzLCBsaWtlIHdpbmQgYW5kIHByZWNpcGl0YXRpb24uKQpgYGB7cn0KIyBmaXJzdCBjb3VudCB0aGUgbnVtYmVyIG9mIE5BcyBpbiB0aGUgY29sdW1ucyB0byBpZGVudGlmeSB3aGljaCBuZWVkIHRvIGhhdmUgYSBtb3ZpbmcgYXZlcmFnZSBhcHBsaWVkCnByaW50KHBhc3RlKHN1bShpcy5uYShLVEckVF9taW4pKSwiTkFzIGluIFRfbWluIikpCnByaW50KHBhc3RlKHN1bShpcy5uYShLVEckVF9tYXgpKSwiTkFzIGluIFRfbWF4IikpCnByaW50KHBhc3RlKHN1bShpcy5uYShLVEckVF9hdmcpKSwiTkFzIGluIFRfYXZnIikpCnByaW50KHBhc3RlKHN1bShpcy5uYShLVEckaHVtaWRpdHkpKSwiTkFzIGluIGh1bWlkaXR5IikpCnByaW50KHBhc3RlKHN1bShpcy5uYShLVEckcHJlY2lwKSksIk5BcyBpbiBwcmVjaXAiKSkKcHJpbnQocGFzdGUoc3VtKGlzLm5hKEtURyRzdW5zaGluZSkpLCJOQXMgaW4gc3Vuc2hpbmUgaG91cnMiKSkKcHJpbnQocGFzdGUoc3VtKGlzLm5hKEtURyR3aW5kX3NwZWVkX2tub3RzKSksIk5BcyBpbiB3aW5kIHNwZWVkIikpCiMgbm90IGNoZWNraW5nIGZvciB3aW5kL2d1c3QgZGlyZWN0aW9uIGJlY2F1c2Ugd2UncmUgbm90IHVzaW5nIHRoYXQgYW55d2hlcmUKYGBgClNvLCBsb29rcyBsaWtlIHdlIG5lZWQgdG8gdXNlIHRoZSBtb3ZpbmcgYXZlcmFnZSBmb3IgZXZlcnkgdmFyaWFibGUgb2YgaW50ZXJlc3QsIHRob3VnaCB0aGVyZSBhcmUgQSBMT1QgbW9yZSBtaXNzaW5nIHZhbHVlcyBmb3Igd2luZCB0aGFuIGZvciBhbnkgb2YgdGhlIG90aGVyIG1lYXN1cmVtZW50cy4KClNvLCB3ZSBjYW4gdXNlIGEgbW92aW5nIGF2ZXJhZ2UgKG1lYW4pIHRvIGZpbGwgaW4gdGhlIG1pc3NpbmcgdmFsdWVzLiBVc2Ugcm9sbGFwcGx5KCkgZnJvbSB0aGUgem9vIHBhY2thZ2UuIENvdWxkIGFsc28gY2hlY2sgb3V0IG5hLmFwcHJveCgpIG9yIG5hLnNwbGluZSgpIGluIHRoZSB6b28gcGFja2FnZSwgd2hpY2ggY291bGQgYmUgYW4gaW50ZXJlc3RpbmcgY29tcGFyaXNvbi4gVGhvdWdoLCB3aXRoIHNvbWUgaW5pdGlhbCB0ZXN0aW5nLCBpdCBzZWVtcyBsaWtlIGl0J3Mgbm90IGZsZXhpYmxlIGVub3VnaD8gSXQgaGFzIGlzc3VlcyB3aXRoIE5BIHZhbHVlcy4gV2UgY291bGQgYWxzbyB0cnkgbmEuYWdncmVnYXRlKCkgd2hpY2ggcmVwbGFjZXMgTkFzIHdpdGggYWdncmVnYXRlZCB2YWx1ZXMsIHNvIHdlIGNvdWxkIHJlcGxhY2UgTkFzIHdpdGggc29tZXRoaW5nIGxpa2UgYSBtb250bHkgbWVhbi4gVGhpcyBpcyBwcm9iYWJseSBub3QgYmV0dGVyIHRoYW4gYSBtb3ZpbmcgYXZlcmFnZS4gWWV0IGFub3RoZXIgb3B0aW9uIGlzIGdpdmVuIGJ5IG5hLmxvY2YoKSB3aGljaCBmaWxscyBpbiB0aGUgTkFzIHdpdGggdGhlIGxhc3Qgbm9uLU5BIHZhbHVlLiAKYGBge3J9CiMgbWFrZSBhIGRhdGEgZnJhbWUgdG8gc3RvcmUgdGhlIHJvbGxpbmcgbWVhbnMgaW4KIyBhbHNvIGhhdmUgdGhlIG9yaWdpbmFsIGRhdGEgaW4gdGhlcmUgTkVYVCBUTyBUSEUgUk9MTElORyBNRUFOIHNvIGl0J3MgZWFzaWVyIHRvIGNvbXBhcmUgdGhlIHJlc3VsdHMKS1RHX3JvbGxtZWFuczwtZGF0YS5mcmFtZShtYXRyaXgobnJvdz1sZW5ndGgoS1RHJGRhdGUpLG5jb2w9MSsoNyoyKSkpICAgICMgc2FtZSBudW1iZXIgb2Ygcm93cyBhcyBvcmlnaW5hbCwgYW5kIHRoZW4gMiBjb2x1bW5zIGZvciBlYWNoIG9mIHRoZSA3IHZhcmlhYmxlcyBhbmQgaGF2ZSBhIGNvbHVtbiBmb3IgdGhlIGRhdGUKCiMgYWRkIGluIHRoZSBkYXRlcwpLVEdfcm9sbG1lYW5zWywxXTwtS1RHJGRhdGUKCiMgc2V0IHRoZSB3aW5kb3cKd2luZG93PC0xMCAgICAKS1RHX3JvbGxtZWFuc1ssMl08LUtURyRUX21pbgpLVEdfcm9sbG1lYW5zWywzXTwtcm9sbGFwcGx5KEtURyRUX21pbix3aW5kb3csRlVOPW1lYW4sZmlsbD1OQSxwYXJ0aWFsPTEsbmEucm09VCkKS1RHX3JvbGxtZWFuc1ssNF08LUtURyRUX21heApLVEdfcm9sbG1lYW5zWyw1XTwtcm9sbGFwcGx5KEtURyRUX21heCx3aW5kb3csRlVOPW1lYW4sZmlsbD1OQSxwYXJ0aWFsPTEsbmEucm09VCkgCktUR19yb2xsbWVhbnNbLDZdPC1LVEckVF9hdmcKS1RHX3JvbGxtZWFuc1ssN108LXJvbGxhcHBseShLVEckVF9hdmcsd2luZG93LEZVTj1tZWFuLGZpbGw9TkEscGFydGlhbD0xLG5hLnJtPVQpIApLVEdfcm9sbG1lYW5zWyw4XTwtS1RHJGh1bWlkaXR5CktUR19yb2xsbWVhbnNbLDldPC1yb2xsYXBwbHkoS1RHJGh1bWlkaXR5LHdpbmRvdyxGVU49bWVhbixmaWxsPU5BLHBhcnRpYWw9MSxuYS5ybT1UKSAKS1RHX3JvbGxtZWFuc1ssMTBdPC1LVEckcHJlY2lwCktUR19yb2xsbWVhbnNbLDExXTwtcm9sbGFwcGx5KEtURyRwcmVjaXAsd2luZG93LEZVTj1tZWFuLGZpbGw9TkEscGFydGlhbD0xLG5hLnJtPVQpIApLVEdfcm9sbG1lYW5zWywxMl08LUtURyRzdW5zaGluZQpLVEdfcm9sbG1lYW5zWywxM108LXJvbGxhcHBseShLVEckc3Vuc2hpbmUsd2luZG93LEZVTj1tZWFuLGZpbGw9TkEscGFydGlhbD0xLG5hLnJtPVQpIApLVEdfcm9sbG1lYW5zWywxNF08LUtURyR3aW5kX3NwZWVkX2tub3RzCktUR19yb2xsbWVhbnNbLDE1XTwtcm9sbGFwcGx5KEtURyR3aW5kX3NwZWVkX2tub3RzLHdpbmRvdyxGVU49bWVhbixmaWxsPU5BLHBhcnRpYWw9MSxuYS5ybT1UKSAKCiMgcmVuYW1lIHRoZSBjb2x1bW5zCiMgd2lsbCBiZSBjb21ibyBvZiBvcmlnaW5hbCBuYW1lcyBwbHVzIG5vdGluZyB0aGF0IGl0J3MgYSByb2xsaW5nIG1lYW4KbmFtZXNfb2c8LWNvbG5hbWVzKEtURylbLShjKDEsMiwzLDExLDEyLDEzKSldICAgICAjIGdldCB0aGUgb3JpZ2luYWwgbmFtZXMgYnV0IGRvbid0IG5lZWQgZmlyc3QgdHdvIGNvbHVtbnMgKHN0YXRpb24gbmFtZSAmIGlkKSBvciB0aGUgY29sdW1ucyB3aXRoIHdpbmQgZGlyZWN0aW9ucy4gZG9uJ3QgbmVlZCBkYXRlIGVpdGhlciBiZWNhdXNlIHdlIGRvbid0IHdhbnQgdG8gYXBwZW5kIHJvbGwgbWVhbiB0byB0aGF0CiMgYWxzbyBub3QgZ29pbmcgdG8gdXNlIHdpbmQgZ3VzdHMgc28gZG9uJ3QgaW5jbHVkZSB0aGF0Cm5hbWVzX25ldzwtcGFzdGUobmFtZXNfb2csInJvbGxtZWFuMTAiLHNlcD0iXyIpICAgIyBhZGQgcm9sbGluZyBtZWFuIHRvIHRoZSBuYW1lcwoKIyBub3cgYSBmb3IgbG9vcCB0byBjb21iaW5lIHRoZSBvcmlnaW5hbCBhbmQgbmV3IG5hbWVzIGluIHRoZSBjb3JyZWN0IG9yZGVyCm5hbWVzX3JvbGxtZWFuczwtTkEgICAgICAjIHdoZXJlIHRoZSBuZXcgbmFtZXMgd2lsbCBiZSBzdG9yZWQKaTwtMSAgICAgICMgY291bnRlciB0aGF0IGtlZXBzIHRyYWNrIG9mIHdoaWNoIGluZGV4IGluIHRoZSBuZXcgY29tYmluZWQgbmFtZXMgdmVjdG9yIHdlIGFyZSBhdAoKZm9yKGNvbCBpbiAxOmxlbmd0aChuYW1lc19uZXcpKXsgICAjIGNvbCBrZWVwcyB0cmFjayBvZiBpbmRleCBpbiB0aGUgdmVjdG9ycyB3ZSdyZSBwdWxsaW5nIGZyb20gCiAgICBuYW1lc19yb2xsbWVhbnNbaV08LW5hbWVzX29nW2NvbF0KICAgIGk8LWkrMQogICAgbmFtZXNfcm9sbG1lYW5zW2ldPC1uYW1lc19uZXdbY29sXQogICAgaTwtaSsxCn0KCiMgYWRkIGRhdGUgYXQgdGhlIGJlZ2lubmluZwpuYW1lc19yb2xsbWVhbnM8LWMoImRhdGUiLG5hbWVzX3JvbGxtZWFucykKCiMgbm93IHJlbmFtZSB0aGUgY29sdW1ucwpjb2xuYW1lcyhLVEdfcm9sbG1lYW5zKTwtbmFtZXNfcm9sbG1lYW5zCgojIHByaW50IHRoZSBuZXcgbnVtYmVyIG9mIE5BcyAodGhvdWdoIHRoZXkncmUgYWN1dGFsbHkgTmFOcywgbm90IE5BcywgYmVjYXVzZSB0aGF0IGlzIHdoYXQgcm9sbHN1bSgpIHB1dHMgb3V0LikKcHJpbnQocGFzdGUoc3VtKGlzLm5hKEtUR19yb2xsbWVhbnMkVF9taW5fcm9sbG1lYW4xMCkpLCJOQXMgaW4gVF9taW5fcm9sbG1lYW4xMCIpKQpwcmludChwYXN0ZShzdW0oaXMubmEoS1RHX3JvbGxtZWFucyRUX21heF9yb2xsbWVhbjEwKSksIk5BcyBpbiBUX21heF9yb2xsbWVhbjEwIikpCnByaW50KHBhc3RlKHN1bShpcy5uYShLVEdfcm9sbG1lYW5zJFRfYXZnX3JvbGxtZWFuMTApKSwiTkFzIGluIFRfYXZnX3JvbGxtZWFuMTAiKSkKcHJpbnQocGFzdGUoc3VtKGlzLm5hKEtUR19yb2xsbWVhbnMkaHVtaWRpdHlfcm9sbG1lYW4xMCkpLCJOQXMgaW4gaHVtaWRpdHlfcm9sbG1lYW4xMCIpKQpwcmludChwYXN0ZShzdW0oaXMubmEoS1RHX3JvbGxtZWFucyRwcmVjaXBfcm9sbG1lYW4xMCkpLCJOQXMgaW4gcHJlY2lwX3JvbGxtZWFuMTAiKSkKcHJpbnQocGFzdGUoc3VtKGlzLm5hKEtUR19yb2xsbWVhbnMkc3Vuc2hpbmVfcm9sbG1lYW4xMCkpLCJOQXMgaW4gc3Vuc2hpbmVfcm9sbG1lYW4xMCIpKQpwcmludChwYXN0ZShzdW0oaXMubmEoS1RHX3JvbGxtZWFucyR3aW5kX3NwZWVkX2tub3RzX3JvbGxtZWFuMTApKSwiTkFzIGluIHdpbmRzcGVlZF9yb2xsbWVhbjEwIikpCiMgbm90IGNoZWNraW5nIGZvciB3aW5kL2d1c3QgZGlyZWN0aW9uIGJlY2F1c2Ugd2UncmUgbm90IHVzaW5nIHRoYXQgYW55d2hlcmUKYGBgCipOT1RFOiogVGhlcmUgc2VlbSB0byBiZSBhIGZldyBwZXJpb2RzIHdoZXJlIHRoZSBlbnRpcmUgd2VhdGhlciBzdGF0aW9uIG11c3QgaGF2ZSBiZWVuIGRvd24sIGJlY2F1c2UgdGhlcmUgYXJlIG5vIHZhbHVlcyBhdCBhbGwgZm9yIGFueSBvZiB0aGUgbWVhc3VyZW1lbnRzIGZvciB0aG9zZSBkYXlzLiBFdmVuIHJvbGwgc3VtIGNhbid0IGRlYWwgd2l0aCB0aGF0Li4uLiB1bmxlc3Mgd2UgdXNlIGEgaHVnZSB3aW5kb3cuIFRob3NlIHRpbWUgcGVyaW9kcyBhcmU6IEp1bHkgMjAwMCBhbmQgU2VwdGVtYmVyIDE5OTcgKHRoZSB3aG9sZSBtb250aHMpLiBUbyBmaWxsIGFsbCB0aG9zZSBpbiB0aGUgd2luZG93IHdvdWxkIG5lZWQgdG8gYmUgb3ZlciAzMCBkYXlzLi4uIGFuZCB0aGF0J3MgcHJvYmFibHkgdG9vIGJpZyEgU28gZm9yIHRoZSAyMCBhbmQgMjEgZGF5cyBpbiB0aGUgbWlkZGxlIG9mIHRoZSBtb250aCB3ZSBkb24ndCBoYXZlIGFueSB2YWx1ZXMgKGJlY2F1c2UgdGhlIGZpcnN0IGFuZCBsYXN0IDUgZG8gaGF2ZSBudW1iZXJzLCBwdWxsZWQgaW4gZnJvbSB0aGUgcHJldmlvdXMgYW5kIGZvbGxvd2luZyBtb250aHMgYnkgdGhlIHJvbGwgYXBwbHkgZnVuY3Rpb24pLiBQbHVzLCB0aGVyZSBhcmUgc2V2ZXJhbCAxMCtkYXkgcGVyaW9kcyB3aGVyZSBubyB3aW5kIG1lYXN1cmVtZW50cyB3ZXJlIHRha2VuOiBKYW4gMTEtIDIxIDE5OTEsIEFwcmlsIDItMTMgMTk5MSwgQXByaWwgMjgtTWF5IDggMTk5MSwgYW5kIE5vdiAyNy1EZWMgNiAxOTkyLiAoQWxzbyBub3RlIHRoYXQgdGhlcmUgYXJlIGEgbG90IG9mIG1pc3NpbmcgdmFsdWVzIG9uIGVpdGhlciBzaWRlIG9mIHRob3NlIHBlcmlvZHMsIHRvbywgYnV0IHRoZXJlIGlzIGF0IGxlYXN0IDEgbWVhc3VyZW1lbnQgZm9yIGV2ZXJ5IDEwIGFwYXJ0IGZyb20gdGhvc2UgY2h1bmtzLikgV2VpcmRseSwgaW4gYSBsb3Qgb2YgdGhlc2UgcGVyaW9kcywgdGhlcmUgSVMgcHJlY2lwaXRhdGlvbiBkYXRhIGJ1dCB0aGUgdmFsdWVzIGFyZSBhbGwgMC4gKFNvIG1heWJlIHRoaXMgaXNuJ3QgYWNjdXJhdGUgZGF0YT8gQ2hlY2tlZCBpbiB0aGUgcmF3IGRhdGEgYW5kIHRoZXkgcmVhbGx5IGFyZSAwcywgbm90IDg4ODgvOTk5OXMuKSBBbmQgdGhlIDQ0IE5Bcy9OYU5zIGZvciBwcmVjaXAgYXJlIGluIDIwMTUsIG5vdCBpbiB0aG9zZSBvdGhlciBsb25nIHN0cmV0Y2hlcy0taXQncyBiYXNpY2FsbHkgdGhlIHNlY29uZCBoYWxmIG9mIDIwMTUsIHdpdGggYSBmZXcgZGF5cyBoZXJlIGFuZCB0aGVyZSB3aGVyZSB0aGVyZSBpcyBkYXRhIHNvIGl0IGlzbid0IGZ1bGx5IE5hTnMuIFNvb28uLi5kb24ndCB1c2UgdGhvc2UgY2h1bmtzIG9mIGRhdGEhIAoKClNvLCB0aGVyZSBhcmUgc29tZSBsaW1pdGF0aW9ucyBldmVuIGFmdGVyIGZpbGxpbmcgaW4gdGhlIG1pc3NpbmcgdmFsdWVzIHdpdGggbW92aW5nIGF2ZXJhZ2VzLi4uIGJ1dCB3ZSBoYXZlIHRvIGdvIGFoZWFkIGFuZCBmaWxsIGluIHRob3NlIHZhbHVlcyBhbnl3YXlzLgpgYGB7cn0KIyBub3cgbWFrZSB0aGUgcmVwbGFjZW1lbnRzISAKIyBmaWd1cmVkIG91dCBob3cgdG8gZG8gdGhpcyB3aXRoIHRoZSB0b3kgZGF0YSBpbiAvVXNlcnMvbGF1cmVuaGVuZHJpY2tzL0RvY3VtZW50cy9Cb3JuZW8vS2V0YXBhbmdfQ2xpbWF0ZUZpcmUvY29kZS9TYW5kYm94Q29kZS9GaWxsTWlzc2luZ1ZhbHVlcy5SCktURyRUX21pbltpcy5uYShLVEckVF9taW4pXTwtS1RHX3JvbGxtZWFucyRUX21pbl9yb2xsbWVhbjEwW2lzLm5hKEtURyRUX21pbildCktURyRUX21heFtpcy5uYShLVEckVF9tYXgpXTwtS1RHX3JvbGxtZWFucyRUX21heF9yb2xsbWVhbjEwW2lzLm5hKEtURyRUX21heCldCktURyRUX2F2Z1tpcy5uYShLVEckVF9hdmcpXTwtS1RHX3JvbGxtZWFucyRUX2F2Z19yb2xsbWVhbjEwW2lzLm5hKEtURyRUX2F2ZyldCktURyRwcmVjaXBbaXMubmEoS1RHJHByZWNpcCldPC1LVEdfcm9sbG1lYW5zJHByZWNpcF9yb2xsbWVhbjEwW2lzLm5hKEtURyRwcmVjaXApXQpLVEckaHVtaWRpdHlbaXMubmEoS1RHJGh1bWlkaXR5KV08LUtUR19yb2xsbWVhbnMkaHVtaWRpdHlfcm9sbG1lYW4xMFtpcy5uYShLVEckaHVtaWRpdHkpXQpLVEckc3Vuc2hpbmVbaXMubmEoS1RHJHN1bnNoaW5lKV08LUtUR19yb2xsbWVhbnMkc3Vuc2hpbmVfcm9sbG1lYW4xMFtpcy5uYShLVEckc3Vuc2hpbmUpXQpLVEckd2luZF9zcGVlZF9rbm90c1tpcy5uYShLVEckd2luZF9zcGVlZF9rbm90cyldPC1LVEdfcm9sbG1lYW5zJHdpbmRfc3BlZWRfa25vdHNfcm9sbG1lYW4xMFtpcy5uYShLVEckd2luZF9zcGVlZF9rbm90cyldCgojIGFsc28gYWRkIGluIGEgY29sdW1uIHRoYXQgZmxhZ3Mgd2hlcmUgdGhlcmUgYXJlIE5BcyBmaWxsZWQgaW4gd2l0aCBhbm90aGVyIHZhbHVlIGluIGF0IGxlYXN0IG9uZSBvZiB0aGUgb3RoZXIgY29sdW1ucwpLVEckZmxhZzwtTkEKS1RHJGZsYWdbaXMubmEoS1RHJFRfbWluKV08LSJPbmUgb3IgbW9yZSBlc3RpbWF0ZWQgdmFsdWVzIgpLVEckZmxhZ1tpcy5uYShLVEckVF9tYXgpXTwtIk9uZSBvciBtb3JlIGVzdGltYXRlZCB2YWx1ZXMiCktURyRmbGFnW2lzLm5hKEtURyRUX2F2ZyldPC0iT25lIG9yIG1vcmUgZXN0aW1hdGVkIHZhbHVlcyIKS1RHJGZsYWdbaXMubmEoS1RHJHByZWNpcCldPC0iT25lIG9yIG1vcmUgZXN0aW1hdGVkIHZhbHVlcyIKS1RHJGZsYWdbaXMubmEoS1RHJGh1bWlkaXR5KV08LSJPbmUgb3IgbW9yZSBlc3RpbWF0ZWQgdmFsdWVzIgpLVEckZmxhZ1tpcy5uYShLVEckc3Vuc2hpbmUpXTwtIk9uZSBvciBtb3JlIGVzdGltYXRlZCB2YWx1ZXMiCktURyRmbGFnW2lzLm5hKEtURyR3aW5kX3NwZWVkX2tub3RzKV08LSJPbmUgb3IgbW9yZSBlc3RpbWF0ZWQgdmFsdWVzIgoKYGBgCgoKTm93IGNoZWNrIHRvIHNlZSBpZiB3ZSBoYXZlIHRoZSBOQXMgZmlsbGVkIGluLiAKYGBge3J9CiMgY2hlY2sgdG8gc2VlIGlmIHRoZSBudW1iZXIgb2YgZmlsbGVkIGluIE5BcyBpcyBsZXNzIHRoYW4gYmVmb3JlCiMgKGlkZWFsbHkgaXQgd291bGQgYmUgMCBidXQgYmVjYXVzZSBvZiB0aGUgaXNzdWUgbm90ZWQgZWFybGllciBhYm91dCB0aGUgMTArIGRheSBzdHJldGNoZXMgb2Ygbm8gdmFsdWVzIHdlIHdvbid0IGV2ZXIgc2VlIDApCnByaW50KHBhc3RlKHN1bShpcy5uYShLVEckVF9taW4pKSwiTkFzIGluIFRfbWluIikpCnByaW50KHBhc3RlKHN1bShpcy5uYShLVEckVF9tYXgpKSwiTkFzIGluIFRfbWF4IikpCnByaW50KHBhc3RlKHN1bShpcy5uYShLVEckVF9hdmcpKSwiTkFzIGluIFRfYXZnIikpCnByaW50KHBhc3RlKHN1bShpcy5uYShLVEckaHVtaWRpdHkpKSwiTkFzIGluIGh1bWlkaXR5IikpCnByaW50KHBhc3RlKHN1bShpcy5uYShLVEckcHJlY2lwKSksIk5BcyBpbiBwcmVjaXAiKSkKcHJpbnQocGFzdGUoc3VtKGlzLm5hKEtURyRzdW5zaGluZSkpLCJOQXMgaW4gc3Vuc2hpbmUgaG91cnMiKSkKcHJpbnQocGFzdGUoc3VtKGlzLm5hKEtURyR3aW5kX3NwZWVkX2tub3RzKSksIk5BcyBpbiB3aW5kIHNwZWVkIikpCmBgYApTbywgaXQncyBub3QgcGVyZmVjdCBidXQgaXQgSVMgYmV0dGVyIHRoYW4gdGhlIG9yaWdpbmFsIGRhdGEhIE1pZ2h0IG5lZWQgdG8gZG8gc29tZXRoaW5nIGxpa2UgYnJlYWsgdGhpcyBpbnRvIHNlY3Rpb25zIHRoYXQgc3RhcnQgYW5kIHN0b3AgYmVmb3JlIGFuZCBhZnRlciB0aGUgYmlnIGNodW5rcyBvZiBtaXNzaW5nIGRhdGEuLi4uIE5lZWQgdG8gdGFsayB3aXRoIERhbiBhYm91dCB0aGF0LiBCdXQgdGhhdCBzaG91bGRuJ3QgYWZmZWN0IHVzIHVudGlsIHdlIGdldCB0byB0aGUgYWV0IGNvYWxjdWxhdGlvbnMsIHNpbmNlIHRoYXQgZG9lcyByZWx5IG9uIHRoZSB2YWx1ZXMgZnJvbSBwcmV2aW91cyBkYXlzLiAKCkJ1dCwgd2UgaGF2ZSB0byBtb3ZlIGFoZWFkIHdpdGggYWxsIG9mIHRoZSBjb252ZXJzaW9ucywgZXRjLiwgYW55d2F5cy4gCgojIENhbGNsdWF0ZSBFVDAKCkZvciBhbGwgb2YgdGhlc2UgY2FsY3VhdGlvbnMsIGFzc3VtZSB0aGF0OiAKKiBBaXJwb3J0IGxvY2F0aW9uIGlzIC0xLjgxNiwgMTA5Ljk2MyAgKDEuODE2IFMsIDEwOS45NjMgRSkKKiBBaXJwb3J0IGVsZXZhdGlvbiBpcyAxNCBtZXRlcnMKClVzZWZ1bCBjb25zdGFudHMvY29udmVyc2lvbnM6IAoqIFdpbmRzcGVlZCBpbiBtZXRlcnMgcGVyIHNlY29uZCA9IDAuNTE0NDQ0NCAqIHdpbmRzcGVlZCBpbiBrbm90cwoqIDEgS1doPTMuNiBNSiAoZm9yIHJhZGlhdGlvbikgIGFrYSAxIHdhdHQtaG91ciA9IDM2MDAgam91bGVzID0gLjAwMzYgTWpvdWxlcwoKT25lIG9idmlvdXMgcHJvYmxlbSB0aGF0IGEgbW92aW5nIGF2ZXJhZ2UgY2FuJ3QgYXQgYWxsIGZpeCBpcyByZWxhdGVkIHRvIHRoZSBob3VycyBvZiBzdW5zaGluZS4gRm9yIG1vc3QgZGF5cyB0aGUgbWF4IHJlcG9ydGVkIGFtb3VudCBvZiBzdW4gaXMgOCBob3VycywgYnV0IHRoaXMgZG9lc24ndCBtYWtlIHNlbnNlIHdpdGggd2hhdCB3ZSBrbm93IGFib3V0IHRoZSBwbGFjZSwgd2hpY2ggaXMgdGhhdCB0aGVyZSBpcyB+MTIgaG91cnMgb2Ygc3VuIHBvc3NpYmxlIGEgZGF5LCBhbmQgaXQgZGVmaW5pdGVseSBpc24ndCBhbHdheXMgY2xvdWR5IGZvciA0IGhvdXJzIGV2ZXJ5IGRheSBpbiBLZXRhcGFuZy4gQWxzbywgdGhlcmUgaXMgb25lIGRheSB3aXRoIDEzLjUgaG91cnMgb2Ygc3Vuc2hpbmUgKDE4IE9jdCAyMDE2KSBhbmQgYW5vdGhlciB3aXRoIDEyLjUgaG91cnMgb2Ygc3Vuc2hpbmUgKDE1IEp1bCAyMDE2KS4gU28gaXQgc2VlbXMgbGlrZWx5IHRoYXQgdGhlcmUgYXJlIHNvbWUgZXJyb3JzIGluIHRoZSBkYXRhLCBidXQgd2UgZG9uJ3Qga25vdyBpZiB0aGV5IGFyZSByYW5kb20gb3Igc3lzdGVtYXRpYyAoZS5nLiwgZGF0YSBlbnRyeSBvciBhIGJpZ2dlciBwcm9ibGVtIGluIGhvdyB0aGV5IGFyZSBtZWFzdXJpbmcgaG91cnMgb2Ygc3Vuc2hpbmUpISBMb29raW5nIGF0IHRoZSByYXcgZGF0YSwgaXQgc2VlbXMgbGlrZSB0aGUgOCBob3VyIG1heCBpc3N1ZSBpcyBwcm9iYWJseSBzb21ldGhpbmcgd2l0aCB0aGUgZGV0ZWN0b3IsIHNpbmNlIH4yMDE1IHRoZSBudW1iZXJzIHN0YXJ0IHRvIG1ha2UgbW9yZSBzZW5zZTsgbWF5YmUgfjIwMTUgdGhleSBnb3QgYSBiZXR0ZXIgZGV0ZWN0b3I/ICBCdXQsIGEgcXVpY2sgc29sdXRpb24gdGhhdCBzdGlsbCBhbGxvd3MgdXMgdG8gdXNlIGFsbCBvZiB0aGUgZGF0YSBpcyB0byBub3JtYWxpemUgYWxsIHRoZSBzdW5zaGluZSBkYXRhIHRvIGEgbWF4aW11bSBvZiA4IGhvdXJzLiAKClRvIGNhbGN1bGF0ZSAqc29sYXIgcmFkaWF0aW9uKiwgd2UgaGF2ZSBpbmZvcm1hdGlvbiBvbiByYWRpYXRpb24gYW5kIGhvdXJzIG9mIHN1bnNoaW5lIGZyb20gIE5BU0EgU3VyZmFjZSBNZXRlb3JvbG9neSBhbmQgU29sYXIgRW5lcmd5IChodHRwczovL2Vvc3dlYi5sYXJjLm5hc2EuZ292L2NnaS1iaW4vc3NlL2dyaWQuY2dpKTogCiogVGhlIG1vbnRobHkgYXZlcmFnZWQgY2xlYXIgc2t5IGluc29sYXRpb24gaW5jaWRlbnQgb24gYSBob3Jpem9udGFsIHN1cmZhY2UgKGtXaC9tMi9kYXkpIGZvciBhaXJwb3J0IGxvY2F0aW9uIGZyb20gMjIgeWVhciBhdmVyYWdlIGlzOiAoSmFuLEZlYixNYXIsQXByLE1heSxKdW4sSnVsLEF1ZyxTZXAsT2N0LE5vdixEZWMpID0gKDcuMzMsNy41NSw3LjUxLDcuMjYsNi44MCw2LjUwLDYuNTcsNi45MSw3LjI4LDcuNDAsNy4zMyw3LjIwKQoqIFRoZSBtb250aGx5IGF2ZXJhZ2VkIGRheWxpZ2h0IGhvdXJzIGZvciBhaXJwb3J0IGxvY2F0aW9uIGZyb20gMjIgeWVhciBhdmVyYWdlIGlzOiAoSmFuLEZlYixNYXIsQXByLE1heSxKdW4sSnVsLEF1ZyxTZXAsT2N0LE5vdixEZWMpID0gKDEyLjIsMTIuMSwxMi4xLDEyLjAsMTIuMCwxMi4wLDEyLjAsMTIuMCwxMi4xLDEyLjEsMTIuMiwxMi4yKQoKVG8gY2FsY3VsYXRlICpkZXdwb2ludCosIHVzZSBzaW1wbGlmaWVkIGVxdWF0aW9uIGZyb20gTGF3cmVuY2UgMjAwNSAobWF5IGxhdGVyIHdhbnQgdG8gdXNlIGEgbW9yZSBwcmVjaXNlIGVxdWF0aW9uKS4gVGhpcyB1c2VzIHJlbGF0aXZlIGh1bWlkaXR5IChzbyBhc3N1bWluZyB0aGF0IHRoZSB2YWx1ZXMgaW4gdGhlIHNwcmVhZHNoZWVkIGFyZSByZWxhdGl2ZSwgbm90IGFic29sdXRlISkgYW5kIHRlbXBlcmF0dXJlLiBUaGUgZXF1YXRpb24gaXM6IGRld3BvaW50IHRlbXBlcmF0dXJlID0gVCAtICgoMTAwIC0gcmVsYXRpdmUgaHVtZGlpdHkpLzUpLiAKCmBgYHtyfQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIERheSBvZiB5ZWFyCiMgY29udmVydCB0aGUgZGF0ZSBpbnRvIHRoZSBkYXkgb2YgdGhlIHllYXIgZm9yIHRoZSBmdW5jdGlvbgpLVEckZG95PC1hcy5udW1lcmljKHN0cmZ0aW1lKEtURyRkYXRlLCBmb3JtYXQgPSAiJWoiKSkKI2hlYWQoS1RHJGRveSkKCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIFdpbmRzcGVlZCAoaW4ga25vdHMpCiMgbm90ZSB0aGF0IHRoZSB3aW5kIHNwZWVkIGlzIGdpdmVuIGluIGtub3RzIGFuZCBpdCBuZWVkcyB0byBiZSBjb252ZXJ0ZWQgdG8gbS9zCiMgd2luZHNwZWVkIGluIG1ldGVycyBwZXIgc2Vjb25kID0gMC41MTQ0NDQ0ICogd2luZHNwZWVkIGluIGtub3RzCktURyR3aW5kX3NwZWVkX21zPC0wLjUxNDQ0NDQqS1RHJHdpbmRfc3BlZWRfa25vdHMKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgU29sYXIgcmFkaWF0aW9uCiMgY2FsY2x1YXRlIHNvbGFyIHJhZGlhdGlvbiBmcm9tIGhvdXJzIG9mIHN1bnNoaW5lCiMjIyMjIyB0cmVhdCA4IGFzIHRoZSBtYXggbnVtYmVyIG9mIGhvdXJzIGluIHRoZSBkYXkgYW5kIHJlZHVjZSBldmVyeXRoaW5nIHRoYXQgaXMgbW9yZSB0aGFuIDggaG91cnMgZG93biB0byA4CktURyRwc3VuPC1pZmVsc2UoS1RHJHN1bnNoaW5lPDgsKEtURyRzdW5zaGluZS84KSwxKQpLVEckc3Vuc2hpbmVfbm9ybTwtOCpLVEckcHN1bgojIyMjIyMgdGhlbiB0byB0dXJuIGl0IGludG8gcmFkaWF0aW9uCiMgdXNlIHRoZSBjbGVhciBza3kgaW5zb2xhdGlvbiwgaG91cnMgb2YgZGF5bGlnaHQsIGFuZCAobm9ybWFsaXplZCkgc3Vuc2hpbmUgaG91cnMgdG8gY2FsY3VhdGUgYSByb3VnaCByYWRpYXRpb24KIyBtb250aGx5IGF2ZXJhZ2VkIGNsZWFyIHNreSBpbnNvbGF0aW9uIGluY2lkZW50IG9uIGEgaG9yaXpvbnRhbCBzdXJmYWNlIChrV2gvbTIvZGF5KSBmb3IgYWlycG9ydCBsb2NhdGlvbjogKEphbixGZWIsTWFyLEFwcixNYXksSnVuLEp1bCxBdWcsU2VwLE9jdCxOb3YsRGVjKT0oNy4zMyw3LjU1LDcuNTEsNy4yNiw2LjgwLDYuNTAsNi41Nyw2LjkxLDcuMjgsNy40MCw3LjMzLDcuMjApCiMgbW9udGhseSBhdmVyYWdlZCBkYXlsaWdodCBob3VycyBmb3IgYWlycG9ydCBsb2NhdGlvbiAoLTEuODE2LCAxMDkuOTYzKSBmcm9tIDIyIHllYXIgYXZlcmFnZSBpczogKEphbixGZWIsTWFyLEFwcixNYXksSnVuLEp1bCxBdWcsU2VwLE9jdCxOb3YsRGVjKT0oMTIuMiwxMi4xLDEyLjEsMTIuMCwxMi4wLDEyLjAsMTIuMCwxMi4wLDEyLjEsMTIuMSwxMi4yLDEyLjIpCiMgMSB3YXR0LWhvdXIgPSAzNjAwIGpvdWxlcyA9IC4wMDM2IE1qb3VsZXMKIyBib3RoIG9mIHRoZXNlIHNldHMgb2YgdmFsdWVzIGFyZSBmcm9tIE5BU0EgU3VyZmFjZSBNZXRlb3JvbG9neSBhbmQgU29sYXIgRW5lcmd5IChodHRwczovL2Vvc3dlYi5sYXJjLm5hc2EuZ292L2NnaS1iaW4vc3NlL2dyaWQuY2dpKQojIG1ha2UgYSBkYXRhIGZyYW1lIHdpdGggdGhlIG1vbnRoLCBpbnNvbGF0aW9uLCBhbmQgaG91cnMgb2YgZGF5bGlnaHQKbW9udGg8LXNlcSgxLDEyLDEpICAjIHRoZSBtb250aCBudW1iZXIKaW5zb2xhdGlvbjwtYyg3LjMzLDcuNTUsNy41MSw3LjI2LDYuODAsNi41MCw2LjU3LDYuOTEsNy4yOCw3LjQwLDcuMzMsNy4yMCkgIyB0aGUgaW5zb2xhdGlvbiB2YWx1ZXMKZGF5bGlnaHQ8LWMoMTIuMiwxMi4xLDEyLjEsMTIuMCwxMi4wLDEyLjAsMTIuMCwxMi4wLDEyLjEsMTIuMSwxMi4yLDEyLjIpICAgIyB0aGUgbWF4aW11bSBkYXlsaWdodCBob3VycwojIyBub3RlIHRoYXQgd2UgY291bGQgYWxzbyB1c2UgdGhlIGRheWxlbmd0aCBmdW5jdGlvbiBmcm9tIHRoZSBwaGVubyBwYWNrYWdlIHRvIGNhbGN1bGF0ZSB0aGUgZGF5bGlnaHQgaG91cnMKdG1wPC1jYmluZC5kYXRhLmZyYW1lKG1vbnRoLGluc29sYXRpb24sZGF5bGlnaHQpCiMgcHVsbCBvdXQganVzdCB0aGUgbW9udGggZnJvbSB0aGUgZGF0ZXMKS1RHJG1vbnRoPC1hcy5udW1lcmljKGZvcm1hdChLVEckZGF0ZSwiJW0iKSkKI2hlYWQoS1RHJG1vbnRoKSAgIyBjaGVjayB0byBtYWtlIHN1cmUgaXQncyBydW5uaW5nIGNvcnJlY3RseSAgIAojIHRoZW4gdXNlICJqb2luIiBmcm9tIHRoZSBwbHlyIHBhY2thZ2UgdG8gY29tYmluZSB0aGUgdHdvIGRhdGEgc2V0cyBiYXNlZCBvbiB0aGUgbW9udGggdmFsdWUsIGFuZCBhbHNvIHByZXNlcnZlIHRoZSBvcmlnaW5hbCByb3cgb3JkZXIKS1RHPC1qb2luKEtURyx0bXAsYnk9Im1vbnRoIikKIyBsYXN0LCBjYWxjdWxhdGUgdGhlIHJhZGlhdGlvbiBiYXNlZCBvbiB0aGUgcGVyY2VudGFnZSBvZiB0b3RhbCBkYXlsaWdodCBob3VycyBzdW5zaGluZSB3YXMgb2JzZXJ2ZWQgbXVsdGlwbGllZCBieSB0aGUgY2xlYXIgc2t5IGluc29sYXRpb24sIGFuZCB0aGVuIGNvbnZlcnQgaXQgdG8gTUogKDFLV2g9My42TUopCktURyRyYWRpYXRpb248LSgoKEtURyRwc3VuKSpLVEckZGF5bGlnaHQpKktURyRpbnNvbGF0aW9uKSozLjYKIyBjYWxjdWxhdGVzIHRoZSBob3VycyBvZiBzdW5zaGluZSBhcyBhIHBlcmNlbnRhZ2UgYW5kIHRoZW4gY29udmVydHMgdGhhdCB0byB3aGF0IGl0IHdvdWxkIGJlIGlmIGl0IHdhcyB0aGUgcmVhbCBkYXlsaWdodCBob3Vycy4uLiB0aGlzIGlzIGEgYmlnIGFzc3VtcHRpb24hISEhCgojIHJlbW92ZSB0aGUgaW50ZXJtZWRpYXRlIGNvbHVtbnMgd2UgYWRkZWQgZm9yIG1vbnRoLCBpbnNvbGF0aW9uLCBhbmQgaG91cnMgb2YgZGF5bGlnaHQKS1RHJG1vbnRoPC1OVUxMCktURyRpbnNvbGF0aW9uPC1OVUxMCktURyRkYXlsaWdodDwtTlVMTAoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyBMYXRpdHVkZQojIEtldGFwYW5nIGFpcnBvcnQgaXMgYXQgYXBwcm94aW1hdGVseSAxLjgxNjTCsFMgLS0+IC0xLjgxNjQKbGF0PC0gLTEuODE2NAoKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMgRWxldmF0aW9uCiMgS2V0YXBhbmcgYWlycG9ydCBlbGV2YXRpb24gaXMgYXBwcm94aW1hdGVseSAxNCBtZXRlcnMgKG5lZWQgdG8gZG91YmxlIGNoZWNrIHRoaXMpCmVsZXY8LTE0CgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIERld3BvaW50CiMgY2FsY3VsYXRlIGRld3BvaW50IHVzaW5nIHRoZSBzaW1wbGlmaWNhdGlvbiBwcmVzZW50ZWQgaW4gTGF3cmVuY2UgMjAwNSAobWF5IGxhdGVyIHdhbnQgdG8gdXNlIGEgbW9yZSBwcmVjaXNlIGVxdWF0aW9uKQojIHRoaXMgdXNlcyByZWxhdGl2ZSBodW1pZGl0eSAoc28gYXNzdW1pbmcgdGhhdCB0aGUgdmFsdWVzIGluIHRoZSBzcHJlYWRzaGVlZCBhcmUgcmVsYXRpdmUsIG5vdCBhYnNvbHV0ZSEpIGFuZCB0ZW1wZXJhdHVyZQojIGRld3BvaW50IHRlbXBlcmF0dXJlID0gVCAtICgoMTAwIC0gcmVsYXRpdmUgaHVtZGlpdHkpLzUpCiMgVCBpcyB0aGUgbWluaW11bSB0ZW1wZXJhdHVyZSAoY2hlY2sgdGhpcykKS1RHJGRld3BvaW50PC1LVEckVF9taW4tKCgxMDAtS1RHJGh1bWlkaXR5KS81KQojaGVhZChLVEckZGV3cG9pbnQpCmBgYAoKQmVjYXVzZSB0aGUgYWV0IGZ1bmN0aW9uIHRha2VzIHZhbHVlcyBmcm9tIHByZXZpb3VzIGRheShzKSBpbnRvIGFjY291bnQsIGN1dCBkb3duIHRvIG9ubHkgdGhlIHRpbWUgcGVyaW9kcyB0aGF0IHdlIGtub3cgYXJlIGdvb2QgYmVmb3JlIGNhbGN1bGF0aW5nIHRoZSBhZXQuIExldCdzIHRha2UgTm92ZW1iZXIgMjAwMSB0aHJvdWdoIEZlYnJ1YXJ5IDIwMTUuIAoKYGBge3J9CktUR18wMHRvMTU8LUtUR1soS1RHJGRhdGU+PWFzLkRhdGUoIjAxLzExLzIwMDAiLCBmb3JtYXQ9IiVkLyVtLyVZIix0ej0iVVRDKzciKSAmIEtURyRkYXRlPGFzLkRhdGUoIjAzLzAxLzIwMTUiLCBmb3JtYXQ9IiVkLyVtLyVZIix0ej0iVVRDKzciKSksXQpgYGAKCgpUaGVuIGRvIHRoZSBjYWxjdWxhdGlvbiBmb3IgcmVmZXJlbmNlIGV2YXBvdHJhbnNwaXJhdGlvbiwgdXNpbmcgYm90aCB0aGUgZnVuY3Rpb24gZGlyZWN0bHkgZnJvbSBEb2Jyb3dza2kgZXQgYWwgYW5kIHRoZSBvbmUgZnJvbSBEYW4uIElkZWFsbHkgdGhleSB3b3VsZCBjb21lIHVwIHdpdGggdGhlIHNhbWUgbnVtYmVycyBhbmQgcGxvdHRpbmcgdGhlbSB3b3VsZCByZXN1bHQgaW4gYSBzdHJhaWdodCBsaW5lLiAKCmBgYHtyfQojIHVzaW5nIHRoZSBEb2Jyb3dza2kgZXQgYWwgZnVuY3Rpb24gY2FsY3VsYXRlIHJlZmVyZW5jZSBldmFwb3RyYW5zcGlyYXRpb24KRVQwX291dHB1dDwtZGFpbHlFVDAobGF0PWxhdCxlbGV2PWVsZXYscHN1bj1LVEdfMDB0bzE1JHBzdW4sd2luZD1LVEdfMDB0bzE1JHdpbmRfc3BlZWRfbXMsZG95PUtUR18wMHRvMTUkZG95LHRtYXg9S1RHXzAwdG8xNSRUX21heCx0bWluPUtUR18wMHRvMTUkVF9taW4scmg9S1RHXzAwdG8xNSRodW1pZGl0eSkKCiMgdXNpbmcgRG9icm93c2tpIGV0IGFsIHZlcnNpb24KRVQwX291dHB1dF9Eb2Jyb3dza2k8LWRhaWx5RVQwX0RvYnJvd3NraShyYWRpYXRpb249S1RHXzAwdG8xNSRyYWRpYXRpb24sdG1heD1LVEdfMDB0bzE1JFRfbWF4LHRtaW49S1RHXzAwdG8xNSRUX21pbix3aW5kPUtUR18wMHRvMTUkd2luZF9zcGVlZF9tcyxsYXQ9bGF0LGVsZXY9ZWxldixkcHQ9S1RHXzAwdG8xNSRkZXdwb2ludCxkb3k9S1RHXzAwdG8xNSRkb3kpCiAgICAgCiAgICAgCiMgdGhlbiBjb21wYXJlIHRoZW0uIApwbG90KEVUMF9vdXRwdXQkZXQwLEVUMF9vdXRwdXRfRG9icm93c2tpLzEwKQojIHRoZSBhZ3JlZW1lbnQgaXMgZ2VuZXJhbGx5IHByZXR0eSBnb29kIHRob3VnaCBpdCdzIHN0aWxsIG9mZiBieSBhIGZhY3RvciBvZiAxMCwgbmVlZCB0byBmaWd1cmUgdGhpcyBvdXQgZXZlbnR1YWxseQpgYGAKQ2xlYXJseSwgdGhlcmUgYXJlIHNvbWUgYmlnIGRpZmZlcmVuY2VzIGluIHRoZSBhc3N1bXB0aW9ucyBtYWRlLCB3aGljaCBpcyBwdXR0aW5nIHRoaW5ncyBvbiBkaWZmZXJlbnQgc2NhbGVzLS1zZWVtcyBsaWtlIHRoZSBEb2Jyb3dza2kgZXQgYWwuIHZlcnNpb24gaXMgMTAgdGltZXMgaGlnaGVyIHRoYW4gRGFuJ3MtLWJ1dCBvdGhlciB0aGFuIHRoYXQsIHRoZSByZWxhdGlvbnNoaXAgaXMgcHJldHR5IGxpbmVhci4gU2VlbXMgb2ZmIGJ5IGEgZmFjdG9yIG9mIH4xMC4gTm90IHlldCBzdXJlIHdoeS4uLiBidXQgbW9zdCBsaWtlbHkgZHVlIHRvIGVpdGhlciB0aGUgcmFkaWF0aW9uIG9yIHRoZSBkZXdwb2ludCBjYWxjbHVhdGlvbnMsIGJlY2F1c2UgdGhvc2UgYXJlIHRoZSBvbmx5IGRpZmZlcmVudCBpbnB1dHMgKGNhbGN1bGF0ZWQgaW5zaWRlIERhbidzIGZ1bmN0aW9uLCBidXQgZ2l2ZW4gYXMgaW5wdXRzIHRvIHRoZSBEb2Jyb3dza2kgZnVuY3Rpb24pLiAKCkRBTiBTQVlTIFRPIFVTRSBISVMgRlVOQ1RJT04gQkVDQVVTRSBUSEUgTlVNQkVSUyBBUkUgTU9SRSBSRUFTT05BQkxFLiAKCiMgQ2FsY2x1YXRlIGFjdHVhbCBldmFwb3RyYW5zcGlyYXRpb24gKGFldCksIGRlZmljaXQsIGV0Yy4gCgpUaGVuIGNhbGN1bGF0ZSBhY3R1YWwgZXZhcG90cmFuc3BpcmF0aW9uIGV0Yy4gdXNpbmcgRGFuJ3MgZnVuY3Rpb24uIApgYGB7cn0KIyBqdXN0IHRyeWluZyBhIHJhbmRvbSB2YWx1ZSBmb3IgYXdjIC0tPiBpdCdzIHRoZSBhdmVyYWdlIHZhbHVlIGluIGh0dHA6Ly9jaXRlc2VlcnguaXN0LnBzdS5lZHUvdmlld2RvYy9kb3dubG9hZD9kb2k9MTAuMS4xLjQ2MS4xMzcxJnJlcD1yZXAxJnR5cGU9cGRmCnNvaWxiYWxhbmNlMTIyPC1hZXRtb2QoZXQwPUVUMF9vdXRwdXQkZXQwLHByZWNpcD1LVEdfMDB0bzE1JHByZWNpcCxhd2M9MTIyKQoKIyBhbHNvIHRyeSBzb21lIG90aGVyIG51bWJlcnMKc29pbGJhbGFuY2U1MDwtYWV0bW9kKGV0MD1FVDBfb3V0cHV0JGV0MCxwcmVjaXA9S1RHXzAwdG8xNSRwcmVjaXAsYXdjPTUwKQpzb2lsYmFsYW5jZTgwPC1hZXRtb2QoZXQwPUVUMF9vdXRwdXQkZXQwLHByZWNpcD1LVEdfMDB0bzE1JHByZWNpcCxhd2M9ODApCnNvaWxiYWxhbmNlMTAwPC1hZXRtb2QoZXQwPUVUMF9vdXRwdXQkZXQwLHByZWNpcD1LVEdfMDB0bzE1JHByZWNpcCxhd2M9MTAwKQoKCmBgYAoKTm93LCB3cml0ZSBvdXQgdGhlIGRhdGEgYWZ0ZXIgYWRkaW5nIGluIHRoZSBFVDAgYW5kIHNvaWwtd2F0ZXIgYmFsYW5jZSBpbmZvcm1hdGlvbi4gCmBgYHtyfQojIHB1dCB0aGUgZXQwIGFuZCBhZXQgZGF0YSBpbnRvIHRoZSBkYXRhIGZyYW1lIHdpdGggYWxsIHRoZSBvdGhlciBkYXRhCktUR18wMHRvMTUkRVQwPC1FVDBfb3V0cHV0JGV0MAoKS1RHXzAwdG8xNSRhZXQ1MDwtc29pbGJhbGFuY2U1MCRhZXQKS1RHXzAwdG8xNSRkZWZpY2l0NTA8LXNvaWxiYWxhbmNlNTAkZGVmCktUR18wMHRvMTUkc29pbDUwPC1zb2lsYmFsYW5jZTUwJHNvaWwKS1RHXzAwdG8xNSRhZXQ4MDwtc29pbGJhbGFuY2U4MCRhZXQKS1RHXzAwdG8xNSRkZWZpY2l0ODA8LXNvaWxiYWxhbmNlODAkZGVmCktUR18wMHRvMTUkc29pbDgwPC1zb2lsYmFsYW5jZTgwJHNvaWw4MApLVEdfMDB0bzE1JGFldDEwMDwtc29pbGJhbGFuY2UxMDAkYWV0CktUR18wMHRvMTUkZGVmaWNpdDEwMDwtc29pbGJhbGFuY2UxMDAkZGVmCktUR18wMHRvMTUkc29pbDEwMDwtc29pbGJhbGFuY2UxMDAkc29pbApLVEdfMDB0bzE1JGFldDEyMjwtc29pbGJhbGFuY2UxMjIkYWV0CktUR18wMHRvMTUkZGVmaWNpdDEyMjwtc29pbGJhbGFuY2UxMjIkZGVmCktUR18wMHRvMTUkc29pbDEyMjwtc29pbGJhbGFuY2UxMjIkc29pbAoKIyByZW1vdmUgYSBmZXcgY29sdW1ucyB0aGF0IGFyZW4ndCBpbXBvcnRhbnQgZm9yIGFueXRoaWduIGluIHRoZSBmdXR1cmUgYmVmb3JlIHdyaXRpbmcgb3V0IHRoZSBkYXRhCktUR18wMHRvMTUkd2luZF9tYXhndXN0X2tub3RzPC1OVUxMCktUR18wMHRvMTUkd2luZF9kaXI8LU5VTEwKS1RHXzAwdG8xNSR3aW5kX21heGd1c3RfZGlyPC1OVUxMCktUR18wMHRvMTUkd2luZF9zcGVlZF9rbm90czwtTlVMTApLVEdfMDB0bzE1JGRveTwtTlVMTAoKd3JpdGUudGFibGUoS1RHXzAwdG8xNSwiL1VzZXJzL2xhdXJlbmhlbmRyaWNrcy9Eb2N1bWVudHMvQm9ybmVvL0tldGFwYW5nX0NsaW1hdGVGaXJlL0tldGFwYW5nX0VUMGFuZEFFVF8wMHRvMTUudHh0IixzZXA9IiwiLHJvdy5uYW1lcyA9IEYpCmBgYAoKIyBDb21iaW5pbmcgRVQwL2FldCB3aXRoIE1PRElTIGFjdGl2ZSBmaXJlIGRldGVjdGlvbnMKCk9rYXkhIE5vdyB3ZSdyZSBnb2luZyB0byByZWFkIGluIHRoZSBuZXcgZGF0YSAoZXZlbiB0aG91Z2ggaXQncyBhbHJlYWR5IGluIHRoZSBtZW1vcnkpIHNvIHRoYXQgaW4gZnV0dXJlIHdlIGNhbiBqdXN0IHN0YXJ0IGF0IHRoaXMgY29kZSBjaHVuay4gCmBgYHtyfQojIyBjcmVhdGUgYSBtaW5pIGZ1bmN0aW9uIHRoYXQgbWFrZXMgdGhlIGRhdGUgb2YgdGhlIGRldGVjdGVkIGZpcmUgYmUgcmVhZCBpbiBhcyBhIFBPU0lYY3QgZGF0ZSwgcmF0aGVyIHRoYW4gYXMgYSBjaGFyYWN0ZXIgb3IgYSBzdHJpbmcgb3IgYSBmYWN0b3IKIyBpdCdzIGJlZW4gcmVmb3JtYXR0ZWQgYnkgUiB3aGVuIGl0IHdhcyBjb252ZXJ0ZWQgdG8gYSB0YWJsZSBiZWZvcmUgc28gd2UgY2FuJ3QgdXNlIHRoZSBzYW1lIGZ1bmN0aW9uCnNldENsYXNzKCd5eXl5LW1tLWRkJykKc2V0QXMoImNoYXJhY3RlciIsInl5eXktbW0tZGQiLCBmdW5jdGlvbihmcm9tKSBhcy5EYXRlKGZyb20sIGZvcm1hdD0iJVktJW0tJWQiLHR6PSJVVEMrNyIpKSAgICAjIGFsc28gc2V0cyB0aGUgdGltZSB6b25lOyByZWFsbHkganVzdCBuZWVkZWQgc28gd2UgZG9uJ3QgZ2V0IGFuIGVycm9yIGJlY2F1c2UgYWxsIG9mIHRoaXMgZGF0YSBpcyBmcm9tIHRoZSBzYW1lIHNwb3QKCiMgbm93IHJlYWQgaW4gdGhlIGRhdGEKd2VhdGhlcl8wMHRvMTU8LXJlYWQuY3N2KCIvVXNlcnMvbGF1cmVuaGVuZHJpY2tzL0RvY3VtZW50cy9Cb3JuZW8vS2V0YXBhbmdfQ2xpbWF0ZUZpcmUvS2V0YXBhbmdfRVQwYW5kQUVUXzAwdG8xNS50eHQiLGhlYWRlcj1ULGNvbENsYXNzZXMgPSBjKCJmYWN0b3IiLCJmYWN0b3IiLCJ5eXl5LW1tLWRkIixyZXAoIm51bWVyaWMiLDYpLCJjaGFyYWN0ZXIiLHJlcCgibnVtZXJpYyIsMTcpKSkKCgojIGRvdWJsZSBjaGVjayB0byBtYWtlIHN1cmUgdGhhdCB0aGlzICBET0VTIGdldCByaWQgb2YgYWxsIG9mIHRoZSBiaWcgTkEgcnVucwpwcmludChwYXN0ZShzdW0oaXMubmEod2VhdGhlcl8wMHRvMTUkVF9taW4pKSwiTkFzIGluIFRfbWluIikpCnByaW50KHBhc3RlKHN1bShpcy5uYSh3ZWF0aGVyXzAwdG8xNSRUX21heCkpLCJOQXMgaW4gVF9tYXgiKSkKcHJpbnQocGFzdGUoc3VtKGlzLm5hKHdlYXRoZXJfMDB0bzE1JFRfYXZnKSksIk5BcyBpbiBUX2F2ZyIpKQpwcmludChwYXN0ZShzdW0oaXMubmEod2VhdGhlcl8wMHRvMTUkaHVtaWRpdHkpKSwiTkFzIGluIGh1bWlkaXR5IikpCnByaW50KHBhc3RlKHN1bShpcy5uYSh3ZWF0aGVyXzAwdG8xNSRwcmVjaXApKSwiTkFzIGluIHByZWNpcCIpKQpwcmludChwYXN0ZShzdW0oaXMubmEod2VhdGhlcl8wMHRvMTUkc3Vuc2hpbmUpKSwiTkFzIGluIHN1bnNoaW5lIGhvdXJzIikpCnByaW50KHBhc3RlKHN1bShpcy5uYSh3ZWF0aGVyXzAwdG8xNSR3aW5kX3NwZWVkX21zKSksIk5BcyBpbiB3aW5kIHNwZWVkIikpCnByaW50KHBhc3RlKHN1bShpcy5uYSh3ZWF0aGVyXzAwdG8xNSRFVDApKSwiTkFzIGluIEVUMCIpKQpgYGAKU28sIHdlIGRvbid0IGhhdmUgYW55IE5BIHZhbHVlcyEgWWF5ISAKCkxldCdzIGRvIHNvbWUgdmlzdWFsaXppbmcgb2YgdGhlIGRhdGEuIApgYGB7cn0KIyBldDAKcGxvdCh3ZWF0aGVyXzAwdG8xNSRkYXRlLHdlYXRoZXJfMDB0bzE1JEVUMCx0eXBlPSJwIixwY2g9MTYsY2V4PTAuMjUseWxhYj0iRVQwICIseGxhYj0iRGF0ZSIsIG1haW49IkVUMCBPdmVyIFRpbWUiKQoKIyBBRVQgNTAsIDgwLCAxMDAsIDEyMiAoc2VwYXJhdGVseSkKcGxvdCh3ZWF0aGVyXzAwdG8xNSRkYXRlLHdlYXRoZXJfMDB0bzE1JGFldDUwLHR5cGU9InAiLHBjaD0xNixjZXg9MC4yNSx5bGFiPSJBRVQgPSA1MCIseGxhYj0iRGF0ZSIsIG1haW49IkFFVCBPdmVyIFRpbWUsIEFXQyA9IDUwIikKcGxvdCh3ZWF0aGVyXzAwdG8xNSRkYXRlLHdlYXRoZXJfMDB0bzE1JGFldDgwLHR5cGU9InAiLHBjaD0xNixjZXg9MC4yNSx5bGFiPSJBRVQgPSA4MCIseGxhYj0iRGF0ZSIsIG1haW49IkFFVCBPdmVyIFRpbWUsIEFXQyA9IDgwIikKcGxvdCh3ZWF0aGVyXzAwdG8xNSRkYXRlLHdlYXRoZXJfMDB0bzE1JGFldDEwMCx0eXBlPSJwIixwY2g9MTYsY2V4PTAuMjUseWxhYj0iQUVUID0gMTAwIix4bGFiPSJEYXRlIiwgbWFpbj0iQUVUIE92ZXIgVGltZSwgQVdDID0gMTAwIikKcGxvdCh3ZWF0aGVyXzAwdG8xNSRkYXRlLHdlYXRoZXJfMDB0bzE1JGFldDEyMix0eXBlPSJwIixwY2g9MTYsY2V4PTAuMjUseWxhYj0iQUVUID0gMTIyIix4bGFiPSJEYXRlIiwgbWFpbj0iQUVUIE92ZXIgVGltZSwgQVdDID0gMTIyIikKCiMganVzdCBmb3IgZnVuIGxvb2sgYXQgRVQwIHZzIGFldApwbG90KHdlYXRoZXJfMDB0bzE1JEVUMCx3ZWF0aGVyXzAwdG8xNSRhZXQ1MCx0eXBlPSJwIixwY2g9MTYsY2V4PTAuMjUseWxhYj0iQUVUID0gNTAiLHhsYWI9IkRhdGUiLCBtYWluPSJFVDAgdnMuIEFFVCwgQVdDID0gNTAiKQpwbG90KHdlYXRoZXJfMDB0bzE1JEVUMCx3ZWF0aGVyXzAwdG8xNSRhZXQ4MCx0eXBlPSJwIixwY2g9MTYsY2V4PTAuMjUseWxhYj0iQUVUID0gODAiLHhsYWI9IkRhdGUiLCBtYWluPSJFVDAgdnMuIEFFVCwgQVdDID0gODAiKQpwbG90KHdlYXRoZXJfMDB0bzE1JEVUMCx3ZWF0aGVyXzAwdG8xNSRhZXQxMDAsdHlwZT0icCIscGNoPTE2LGNleD0wLjI1LHlsYWI9IkFFVCA9IDEwMCIseGxhYj0iRGF0ZSIsIG1haW49IkVUMCB2cy4gQUVULCBBV0MgPSAxMDAiKQpwbG90KHdlYXRoZXJfMDB0bzE1JEVUMCx3ZWF0aGVyXzAwdG8xNSRhZXQxMjIsdHlwZT0icCIscGNoPTE2LGNleD0wLjI1LHlsYWI9IkFFVCA9IDEyMiIseGxhYj0iRGF0ZSIsIG1haW49IkVUMCB2cy4gQUVULCBBV0MgPSAxMjIiKQoKCiMgYWxzbyBwbG90IHNvaWwgZGVmaWNpdCBpbnN0ZWFkIG9mIGFldApwbG90KHdlYXRoZXJfMDB0bzE1JGRhdGUsd2VhdGhlcl8wMHRvMTUkZGVmaWNpdDUwLHR5cGU9InAiLHBjaD0xNixjZXg9MC4yNSx5bGFiPSJEZWZpY2l0LCBBV0MgPSA1MCIseGxhYj0iRGF0ZSIsIG1haW4gPSAiRGVmaWNpdCBvdmVyIHRpbWUsIEFXQyA9IDUwIikKcGxvdCh3ZWF0aGVyXzAwdG8xNSRkYXRlLHdlYXRoZXJfMDB0bzE1JGRlZmljaXQ4MCx0eXBlPSJwIixwY2g9MTYsY2V4PTAuMjUseWxhYj0iRGVmaWNpdCwgQVdDID0gODAiLHhsYWI9IkRhdGUiLCBtYWluID0gIkRlZmljaXQgb3ZlciB0aW1lLCBBV0MgPSA4MCIpCnBsb3Qod2VhdGhlcl8wMHRvMTUkZGF0ZSx3ZWF0aGVyXzAwdG8xNSRkZWZpY2l0MTAwLHR5cGU9InAiLHBjaD0xNixjZXg9MC4yNSx5bGFiPSJEZWZpY2l0LCBBV0MgPSAxMDAiLHhsYWI9IkRhdGUiLCBtYWluID0gIkRlZmljaXQgb3ZlciB0aW1lLCBBV0MgPSAxMDAiKQpwbG90KHdlYXRoZXJfMDB0bzE1JGRhdGUsd2VhdGhlcl8wMHRvMTUkZGVmaWNpdDEyMix0eXBlPSJwIixwY2g9MTYsY2V4PTAuMjUseWxhYj0iRGVmaWNpdCwgQVdDID0gMTIyIix4bGFiPSJEYXRlIiwgbWFpbiA9ICJEZWZpY2l0IG92ZXIgdGltZSwgQVdDID0gMTIyIikKCmBgYAoKTm93IGNvbWJpbmUgd2l0aCB0aGUgTU9ESVMgZGF0YS4gCgoKUmVhZCBpbiB0aGUgbmVjZXNzYXJ5IGxpYnJhcmllcywgYW5kIHNvbWUgY29kZSB0byByZWFkIGluIHRoZSBmaXJlIGRldGVjdGlvbnMgdGhhdCBoYXZlIGFscmVhZHkgYmVlbiBjdXQgZG93biB0byBqdXN0IHRoZSBhcmVhIHdpdGhpbiB0aGUgYnVmZmVyLCBhbmQgbWFrZSB0aGUgY29sdW1ucyBiZSB0aGUgY29ycmVjdCBjbGFzcy4KYGBge3J9CiMgbG9hZCBsaWJyYXJpZXMKIyBpbnN0YWxsIHRoZSBwYWNrYWdlIGlmIHlvdSBkb24ndCBhbHJlYWR5IGhhdmUgaXQKaWYgKCFyZXF1aXJlKCJwbHlyIikpIHsKICAgICBpbnN0YWxsLnBhY2thZ2VzKCJwbHlyIikKICAgICBsaWJyYXJ5KHBseXIpCiAgICAgfQoKIyB0aGVuIHRoZSBNT0RJUyBkYXRhCmZpcmVfNTAwa208LXJlYWQuY3N2KCIvVXNlcnMvbGF1cmVuaGVuZHJpY2tzL0RvY3VtZW50cy9Cb3JuZW8vS2V0YXBhbmdfQ2xpbWF0ZUZpcmUvRlJQX2RhaWx5XzUwMGttLnR4dCIsaGVhZGVyPVQsc2VwPSIsIixjb2xDbGFzc2VzID1jKCJ5eXl5LW1tLWRkIiwibnVtZXJpYyIpKQoKIyByZW5hbWUgdGhlIGRhdGUgY29sdW1uCmNvbG5hbWVzKGZpcmVfNTAwa20pWzFdPC0iZGF0ZSIKCiMgbWFrZSBpdCBtYXRjaCB0aGUgd2VhdGhlciBkYXRhIGluIHRpbWUgZnJhbWUKZmlyZV81MDBrbV8wMHRvMTU8LWZpcmVfNTAwa21bKGZpcmVfNTAwa20kZGF0ZT49YXMuRGF0ZSgiMDEvMTEvMjAwMCIsIGZvcm1hdD0iJWQvJW0vJVkiLHR6PSJVVEMrNyIpICYgZmlyZV81MDBrbSRkYXRlPGFzLkRhdGUoIjAzLzAxLzIwMTUiLCBmb3JtYXQ9IiVkLyVtLyVZIix0ej0iVVRDKzciKSksXQoKYGBgCgpUaGVuIGNvbWJpbmUgdGhlIGRhdGEgc2V0cy4gCmBgYHtyfQojIGNvbWJpbmUgdGVoIHR3byBkYXRhIHNldHMgYnkgdGhlIGRhdGUsIGtlZXBpbmcgdGhlIHdlYXRoZXIgZGF0YSBldmVuIGlmIHRoZXJlIGlzIG5vIHJlcG9ydGVkIEZSUCBmb3IgYSBkYXkKd2VhdGhlcl9maXJlPC1qb2luKGZpcmVfNTAwa21fMDB0bzE1LHdlYXRoZXJfMDB0bzE1LGJ5PSJkYXRlIix0eXBlPSJmdWxsIixtYXRjaD0iYWxsIikKCiMgaWYgd2Ugd2FudGVkICBvcmRlciBpdCBieSBkYXRlIHRoaXMgaXMgaG93IHdlJ2QgZG8gaXQKb3JkZXJlZF93ZWF0aGVyX2ZpcmU8LXdlYXRoZXJfZmlyZVtvcmRlcih3ZWF0aGVyX2ZpcmUkRGF0ZSksXQpgYGAKClRoZW4gcGxvdCB0aGluZ3MhISEgCmBgYHtyfQojIHRoZW4gcGxvdCBpdCEgCnBsb3Qod2VhdGhlcl9maXJlJGFldDUwLHdlYXRoZXJfZmlyZSRUb3RhbF9GUlAsdHlwZT0icCIscGNoPTE2LGNleD0wLjUpCnBsb3Qod2VhdGhlcl9maXJlJGRlZmljaXQ1MCx3ZWF0aGVyX2ZpcmUkVG90YWxfRlJQLHR5cGU9InAiLHBjaD0xNixjZXg9MC41KQpwbG90KHdlYXRoZXJfZmlyZSRhZXQ4MCx3ZWF0aGVyX2ZpcmUkVG90YWxfRlJQLHR5cGU9InAiLHBjaD0xNixjZXg9MC41KQpwbG90KHdlYXRoZXJfZmlyZSRkZWZpY2l0ODAsd2VhdGhlcl9maXJlJFRvdGFsX0ZSUCx0eXBlPSJwIixwY2g9MTYsY2V4PTAuNSkKcGxvdCh3ZWF0aGVyX2ZpcmUkYWV0MTAwLHdlYXRoZXJfZmlyZSRUb3RhbF9GUlAsdHlwZT0icCIscGNoPTE2LGNleD0wLjUpCnBsb3Qod2VhdGhlcl9maXJlJGRlZmljaXQxMDAsd2VhdGhlcl9maXJlJFRvdGFsX0ZSUCx0eXBlPSJwIixwY2g9MTYsY2V4PTAuNSkKcGxvdCh3ZWF0aGVyX2ZpcmUkYWV0MTIyLHdlYXRoZXJfZmlyZSRUb3RhbF9GUlAsdHlwZT0icCIscGNoPTE2LGNleD0wLjUpCnBsb3Qod2VhdGhlcl9maXJlJGRlZmljaXQxMjIsd2VhdGhlcl9maXJlJFRvdGFsX0ZSUCx0eXBlPSJwIixwY2g9MTYsY2V4PTAuNSkKYGBgCgo=